diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 2d45bbc24b..533135e593 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -204,6 +204,7 @@ set( PCBNEW_EXPORTERS exporters/export_gencad.cpp exporters/export_idf.cpp exporters/export_vrml.cpp + exporters/export_footprints_placefile.cpp exporters/gen_drill_report_files.cpp exporters/gen_footprints_placefile.cpp exporters/gendrill_Excellon_writer.cpp diff --git a/pcbnew/dialogs/dialog_gen_footprint_position_file_base.cpp b/pcbnew/dialogs/dialog_gen_footprint_position_file_base.cpp index c17f5d375d..e039968e2c 100644 --- a/pcbnew/dialogs/dialog_gen_footprint_position_file_base.cpp +++ b/pcbnew/dialogs/dialog_gen_footprint_position_file_base.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Feb 10 2019) +// C++ code generated with wxFormBuilder (version Dec 1 2018) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -31,7 +31,7 @@ DIALOG_GEN_FOOTPRINT_POSITION_BASE::DIALOG_GEN_FOOTPRINT_POSITION_BASE( wxWindow m_outputDirectoryName->SetToolTip( _("Target directory for plot files. Can be absolute or relative to the board file location.") ); m_outputDirectoryName->SetMinSize( wxSize( 350,-1 ) ); - bSizerdirBrowse->Add( m_outputDirectoryName, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + bSizerdirBrowse->Add( m_outputDirectoryName, 0, wxALIGN_CENTER_VERTICAL, 5 ); m_browseButton = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 ); m_browseButton->SetMinSize( wxSize( 30,28 ) ); @@ -39,13 +39,13 @@ DIALOG_GEN_FOOTPRINT_POSITION_BASE::DIALOG_GEN_FOOTPRINT_POSITION_BASE( wxWindow bSizerdirBrowse->Add( m_browseButton, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); - bUpperSizer->Add( bSizerdirBrowse, 1, wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT|wxTOP, 10 ); + bUpperSizer->Add( bSizerdirBrowse, 0, wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT|wxTOP, 10 ); m_MainSizer->Add( bUpperSizer, 0, wxEXPAND, 2 ); - wxBoxSizer* bSizer71; - bSizer71 = new wxBoxSizer( wxHORIZONTAL ); + wxBoxSizer* bSizerMiddle; + bSizerMiddle = new wxBoxSizer( wxHORIZONTAL ); wxString m_rbFormatChoices[] = { _("ASCII"), _("CSV") }; int m_rbFormatNChoices = sizeof( m_rbFormatChoices ) / sizeof( wxString ); @@ -53,7 +53,7 @@ DIALOG_GEN_FOOTPRINT_POSITION_BASE::DIALOG_GEN_FOOTPRINT_POSITION_BASE( wxWindow m_rbFormat->SetSelection( 0 ); m_rbFormat->SetMinSize( wxSize( 90,-1 ) ); - bSizer71->Add( m_rbFormat, 1, wxALL, 5 ); + bSizerMiddle->Add( m_rbFormat, 1, wxALL, 5 ); wxString m_radioBoxUnitsChoices[] = { _("Inches"), _("Millimeters") }; int m_radioBoxUnitsNChoices = sizeof( m_radioBoxUnitsChoices ) / sizeof( wxString ); @@ -61,7 +61,7 @@ DIALOG_GEN_FOOTPRINT_POSITION_BASE::DIALOG_GEN_FOOTPRINT_POSITION_BASE( wxWindow m_radioBoxUnits->SetSelection( 0 ); m_radioBoxUnits->SetMinSize( wxSize( 90,-1 ) ); - bSizer71->Add( m_radioBoxUnits, 1, wxALL, 5 ); + bSizerMiddle->Add( m_radioBoxUnits, 1, wxALL, 5 ); wxString m_radioBoxFilesCountChoices[] = { _("Separate files for front and back"), _("Single file for board") }; int m_radioBoxFilesCountNChoices = sizeof( m_radioBoxFilesCountChoices ) / sizeof( wxString ); @@ -69,50 +69,49 @@ DIALOG_GEN_FOOTPRINT_POSITION_BASE::DIALOG_GEN_FOOTPRINT_POSITION_BASE( wxWindow m_radioBoxFilesCount->SetSelection( 0 ); m_radioBoxFilesCount->SetToolTip( _("Creates 2 files: one for each board side or\nCreates only one file containing all footprints to place\n") ); - bSizer71->Add( m_radioBoxFilesCount, 2, wxALL, 5 ); + bSizerMiddle->Add( m_radioBoxFilesCount, 2, wxALL, 5 ); - m_MainSizer->Add( bSizer71, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 ); + m_MainSizer->Add( bSizerMiddle, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 ); - wxBoxSizer* bSizer7; - bSizer7 = new wxBoxSizer( wxVERTICAL ); + wxBoxSizer* bSizerLower; + bSizerLower = new wxBoxSizer( wxVERTICAL ); m_forceSMDOpt = new wxCheckBox( this, wxID_ANY, _("Include footprints with SMD pads even if not marked Surface Mount"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer7->Add( m_forceSMDOpt, 0, wxALL, 5 ); + bSizerLower->Add( m_forceSMDOpt, 0, wxALL, 5 ); m_messagesPanel = new WX_HTML_REPORT_PANEL( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_messagesPanel->SetMinSize( wxSize( 300,150 ) ); + m_messagesPanel->SetMinSize( wxSize( 350,300 ) ); - bSizer7->Add( m_messagesPanel, 1, wxEXPAND | wxALL, 5 ); + bSizerLower->Add( m_messagesPanel, 1, wxEXPAND | wxALL, 5 ); - m_MainSizer->Add( bSizer7, 1, wxEXPAND|wxRIGHT|wxLEFT, 5 ); + m_MainSizer->Add( bSizerLower, 1, wxEXPAND|wxRIGHT|wxLEFT, 5 ); - m_sdbSizer1 = new wxStdDialogButtonSizer(); - m_sdbSizer1OK = new wxButton( this, wxID_OK ); - m_sdbSizer1->AddButton( m_sdbSizer1OK ); - m_sdbSizer1Cancel = new wxButton( this, wxID_CANCEL ); - m_sdbSizer1->AddButton( m_sdbSizer1Cancel ); - m_sdbSizer1->Realize(); + m_sdbSizer = new wxStdDialogButtonSizer(); + m_sdbSizerOK = new wxButton( this, wxID_OK ); + m_sdbSizer->AddButton( m_sdbSizerOK ); + m_sdbSizerCancel = new wxButton( this, wxID_CANCEL ); + m_sdbSizer->AddButton( m_sdbSizerCancel ); + m_sdbSizer->Realize(); - m_MainSizer->Add( m_sdbSizer1, 0, wxALL|wxEXPAND, 5 ); + m_MainSizer->Add( m_sdbSizer, 0, wxALL|wxEXPAND, 5 ); this->SetSizer( m_MainSizer ); this->Layout(); - m_MainSizer->Fit( this ); this->Centre( wxBOTH ); // Connect Events m_browseButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GEN_FOOTPRINT_POSITION_BASE::OnOutputDirectoryBrowseClicked ), NULL, this ); - m_sdbSizer1OK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GEN_FOOTPRINT_POSITION_BASE::OnGenerate ), NULL, this ); + m_sdbSizerOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GEN_FOOTPRINT_POSITION_BASE::OnGenerate ), NULL, this ); } DIALOG_GEN_FOOTPRINT_POSITION_BASE::~DIALOG_GEN_FOOTPRINT_POSITION_BASE() { // Disconnect Events m_browseButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GEN_FOOTPRINT_POSITION_BASE::OnOutputDirectoryBrowseClicked ), NULL, this ); - m_sdbSizer1OK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GEN_FOOTPRINT_POSITION_BASE::OnGenerate ), NULL, this ); + m_sdbSizerOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GEN_FOOTPRINT_POSITION_BASE::OnGenerate ), NULL, this ); } diff --git a/pcbnew/dialogs/dialog_gen_footprint_position_file_base.fbp b/pcbnew/dialogs/dialog_gen_footprint_position_file_base.fbp index c416ee03a4..46b370b3da 100644 --- a/pcbnew/dialogs/dialog_gen_footprint_position_file_base.fbp +++ b/pcbnew/dialogs/dialog_gen_footprint_position_file_base.fbp @@ -14,7 +14,6 @@ dialog_gen_footprint_position_file_base 1000 none - 1 dialog_gen_footprint_positions_base @@ -26,7 +25,6 @@ 1 1 UI - 0 1 0 @@ -47,7 +45,7 @@ -1,-1 DIALOG_GEN_FOOTPRINT_POSITION_BASE - -1,-1 + 506,476 wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER DIALOG_SHIM; dialog_shim.h Generate Footprint Position Files @@ -72,7 +70,7 @@ 10 wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT|wxTOP - 1 + 0 bSizerdirBrowse @@ -141,8 +139,8 @@ 5 - wxALIGN_CENTER_VERTICAL|wxEXPAND - 1 + wxALIGN_CENTER_VERTICAL + 0 1 1 @@ -286,7 +284,7 @@ 0 - bSizer71 + bSizerMiddle wxHORIZONTAL none @@ -495,7 +493,7 @@ 1 - bSizer7 + bSizerLower wxVERTICAL none @@ -599,7 +597,7 @@ 0 - 300,150 + 350,300 1 m_messagesPanel 1 @@ -635,7 +633,7 @@ 0 0 - m_sdbSizer1 + m_sdbSizer protected OnGenerate diff --git a/pcbnew/dialogs/dialog_gen_footprint_position_file_base.h b/pcbnew/dialogs/dialog_gen_footprint_position_file_base.h index 220cfcfa21..1ff55d8efb 100644 --- a/pcbnew/dialogs/dialog_gen_footprint_position_file_base.h +++ b/pcbnew/dialogs/dialog_gen_footprint_position_file_base.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Feb 10 2019) +// C++ code generated with wxFormBuilder (version Dec 1 2018) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -50,9 +50,9 @@ class DIALOG_GEN_FOOTPRINT_POSITION_BASE : public DIALOG_SHIM wxRadioBox* m_radioBoxFilesCount; wxCheckBox* m_forceSMDOpt; WX_HTML_REPORT_PANEL* m_messagesPanel; - wxStdDialogButtonSizer* m_sdbSizer1; - wxButton* m_sdbSizer1OK; - wxButton* m_sdbSizer1Cancel; + wxStdDialogButtonSizer* m_sdbSizer; + wxButton* m_sdbSizerOK; + wxButton* m_sdbSizerCancel; // Virtual event handlers, overide them in your derived class virtual void OnOutputDirectoryBrowseClicked( wxCommandEvent& event ) { event.Skip(); } @@ -61,7 +61,7 @@ class DIALOG_GEN_FOOTPRINT_POSITION_BASE : public DIALOG_SHIM public: - DIALOG_GEN_FOOTPRINT_POSITION_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Generate Footprint Position Files"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + DIALOG_GEN_FOOTPRINT_POSITION_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Generate Footprint Position Files"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 506,476 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); ~DIALOG_GEN_FOOTPRINT_POSITION_BASE(); }; diff --git a/pcbnew/exporters/export_footprints_placefile.cpp b/pcbnew/exporters/export_footprints_placefile.cpp new file mode 100644 index 0000000000..2f99171f70 --- /dev/null +++ b/pcbnew/exporters/export_footprints_placefile.cpp @@ -0,0 +1,419 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2019 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* + * 1 - create ascii/csv files for automatic placement of smd components + * 2 - create a module report (pos and module descr) (ascii file) + */ + +#include +#include +#include +#include +#include + + +class LIST_MOD // An helper class used to build a list of useful footprints. +{ +public: + MODULE* m_Module; // Link to the actual footprint + wxString m_Reference; // Its schematic reference + wxString m_Value; // Its schematic value + LAYER_NUM m_Layer; // its side (B_Cu, or F_Cu) +}; + + +// Defined values to write coordinates using inches or mm: +static const double conv_unit_inch = 0.001 / IU_PER_MILS ; // units = INCHES +static const char unit_text_inch[] = "## Unit = inches, Angle = deg.\n"; + +static const double conv_unit_mm = 1.0 / IU_PER_MM; // units = mm +static const char unit_text_mm[] = "## Unit = mm, Angle = deg.\n"; + +// Sort function use by GenereModulesPosition() +// sort is made by side (layer) top layer first +// then by reference increasing order +static bool sortFPlist( const LIST_MOD& ref, const LIST_MOD& tst ) +{ + if( ref.m_Layer == tst.m_Layer ) + return StrNumCmp( ref.m_Reference, tst.m_Reference ) < 0; + + return ref.m_Layer > tst.m_Layer; +} + + +/** + * Helper function HasNonSMDPins + * returns true if the given module has any non smd pins, such as through hole + * and therefore cannot be placed automatically. + */ +static bool HasNonSMDPins( MODULE* aModule ) +{ + for( auto pad : aModule->Pads() ) + { + if( pad->GetAttribute() != PAD_ATTRIB_SMD ) + return true; + } + + return false; +} + + + +enum SELECT_SIDE +{ + PCB_NO_SIDE, + PCB_BACK_SIDE, + PCB_FRONT_SIDE, + PCB_BOTH_SIDES +}; + +PLACE_FILE_EXPORTER::PLACE_FILE_EXPORTER( BOARD* aBoard, bool aUnitsMM, + bool aForceSmdItems, bool aTopSide, + bool aBottomSide, bool aFormatCSV ) +{ + m_board = aBoard; + m_unitsMM = aUnitsMM; + m_forceSmdItems = aForceSmdItems; + + if( aTopSide && aBottomSide ) + m_side = PCB_BOTH_SIDES; + else if( aTopSide ) + m_side = PCB_FRONT_SIDE; + else if( aBottomSide ) + m_side = PCB_BACK_SIDE; + else + m_side = PCB_NO_SIDE; + + m_formatCSV = aFormatCSV; +} + + +std::string PLACE_FILE_EXPORTER::GenPositionData() +{ + std::string buffer; + char line[1024]; // A line to print intermediate data + + // Minimal text lengths: + m_fpCount = 0; + int lenRefText = 8; + int lenValText = 8; + int lenPkgText = 16; + + m_place_Offset = m_board->GetAuxOrigin(); + + // Calculating the number of useful footprints (CMS attribute, not VIRTUAL) + m_fpCount = 0; + + // Select units: + double conv_unit = m_unitsMM ? conv_unit_mm : conv_unit_inch; + const char *unit_text = m_unitsMM ? unit_text_mm : unit_text_inch; + + // Build and sort the list of footprints alphabetically + std::vector list; + m_smdFootprintsNotLabeledSMD.clear(); + + for( auto footprint : m_board->Modules() ) + { + if( m_side != PCB_BOTH_SIDES ) + { + if( footprint->GetLayer() == B_Cu && m_side != PCB_BACK_SIDE ) + continue; + if( footprint->GetLayer() == F_Cu && m_side != PCB_FRONT_SIDE ) + continue; + } + + if( footprint->GetAttributes() & MOD_VIRTUAL ) + continue; + + if( ( footprint->GetAttributes() & MOD_CMS ) == 0 ) + { + if( m_forceSmdItems ) // true to fix a bunch of mis-labeled footprints: + { + if( !HasNonSMDPins( footprint ) ) + { + // all footprint's pins are SMD, mark the part for pick and place + // Note: they are not necessary to pick and place, + // but the probability is high + m_smdFootprintsNotLabeledSMD.push_back( footprint ); + } + else + continue; + } + else + continue; + } + + m_fpCount++; + + LIST_MOD item; + item.m_Module = footprint; + item.m_Reference = footprint->GetReference(); + item.m_Value = footprint->GetValue(); + item.m_Layer = footprint->GetLayer(); + list.push_back( item ); + + lenRefText = std::max( lenRefText, int(item.m_Reference.length()) ); + lenValText = std::max( lenValText, int(item.m_Value.length()) ); + lenPkgText = std::max( lenPkgText, int(item.m_Module->GetFPID().GetLibItemName().length()) ); + } + + if( list.size() > 1 ) + sort( list.begin(), list.end(), sortFPlist ); + + // Switch the locale to standard C (needed to print floating point numbers) + LOCALE_IO toggle; + + if( m_formatCSV ) + { + wxChar csv_sep = ','; + + // Set first line:; + sprintf( line, "Ref%cVal%cPackage%cPosX%cPosY%cRot%cSide\n", + csv_sep, csv_sep, csv_sep, csv_sep, csv_sep, csv_sep ); + + buffer += line; + + for( int ii = 0; ii < m_fpCount; ii++ ) + { + wxPoint footprint_pos; + footprint_pos = list[ii].m_Module->GetPosition(); + footprint_pos -= m_place_Offset; + + LAYER_NUM layer = list[ii].m_Module->GetLayer(); + wxASSERT( layer == F_Cu || layer == B_Cu ); + + wxString tmp = "\"" + list[ii].m_Reference; + tmp << "\"" << csv_sep; + tmp << "\"" << list[ii].m_Value; + tmp << "\"" << csv_sep; + tmp << "\"" << list[ii].m_Module->GetFPID().GetLibItemName().wx_str(); + tmp << "\"" << csv_sep; + + tmp << wxString::Format( "%f%c%f%c%f", + footprint_pos.x * conv_unit, csv_sep, + // Keep the Y axis oriented from bottom to top, + // ( change y coordinate sign ) + -footprint_pos.y * conv_unit, csv_sep, + list[ii].m_Module->GetOrientation() / 10.0 ); + tmp << csv_sep; + + tmp << ( (layer == F_Cu ) ? PLACE_FILE_EXPORTER::GetFrontSideName() + : PLACE_FILE_EXPORTER::GetBackSideName() ); + tmp << '\n'; + + buffer += TO_UTF8( tmp ); + } + } + else + { + // Write file header + sprintf( line, "### Module positions - created on %s ###\n", TO_UTF8( DateAndTime() ) ); + + buffer += line; + + wxString Title = GetBuildVersion(); + sprintf( line, "### Printed by Pcbnew version %s\n", TO_UTF8( Title ) ); + buffer += line; + + buffer += unit_text; + buffer += "## Side : "; + + if( m_side == PCB_BACK_SIDE ) + buffer += GetBackSideName().c_str(); + else if( m_side == PCB_FRONT_SIDE ) + buffer += GetFrontSideName().c_str(); + else if( m_side == PCB_BOTH_SIDES ) + buffer += "All"; + else + buffer += "---"; + + buffer += "\n"; + + sprintf(line, "%-*s %-*s %-*s %9.9s %9.9s %8.8s %s\n", + int(lenRefText), "# Ref", + int(lenValText), "Val", + int(lenPkgText), "Package", + "PosX", "PosY", "Rot", "Side" ); + buffer += line; + + for( int ii = 0; ii < m_fpCount; ii++ ) + { + wxPoint footprint_pos; + footprint_pos = list[ii].m_Module->GetPosition(); + footprint_pos -= m_place_Offset; + + LAYER_NUM layer = list[ii].m_Module->GetLayer(); + wxASSERT( layer == F_Cu || layer == B_Cu ); + + if( layer == B_Cu ) + footprint_pos.x = - footprint_pos.x; + + wxString ref = list[ii].m_Reference; + wxString val = list[ii].m_Value; + wxString pkg = list[ii].m_Module->GetFPID().GetLibItemName(); + ref.Replace( wxT( " " ), wxT( "_" ) ); + val.Replace( wxT( " " ), wxT( "_" ) ); + pkg.Replace( wxT( " " ), wxT( "_" ) ); + sprintf(line, "%-*s %-*s %-*s %9.4f %9.4f %8.4f %s\n", + lenRefText, TO_UTF8( ref ), + lenValText, TO_UTF8( val ), + lenPkgText, TO_UTF8( pkg ), + footprint_pos.x * conv_unit, + // Keep the coordinates in the first quadrant, + // (i.e. change y sign + -footprint_pos.y * conv_unit, + list[ii].m_Module->GetOrientation() / 10.0, + (layer == F_Cu ) ? GetFrontSideName().c_str() : GetBackSideName().c_str() ); + buffer += line; + } + + // Write EOF + buffer += "## End\n"; + } + + return buffer; +} + + +std::string PLACE_FILE_EXPORTER::GenReportData() +{ + std::string buffer; + wxPoint module_pos; + + m_place_Offset = wxPoint( 0, 0 ); + + // Select units: + double conv_unit = m_unitsMM ? conv_unit_mm : conv_unit_inch; + const char *unit_text = m_unitsMM ? unit_text_mm : unit_text_inch; + + LOCALE_IO toggle; + + // Generate header file comments.) + char line[1024]; + sprintf( line, "## Footprint report - date %s\n", TO_UTF8( DateAndTime() ) ); + buffer += line; + + wxString Title = GetBuildVersion(); + sprintf( line, "## Created by Pcbnew version %s\n", TO_UTF8( Title ) ); + buffer += line; + + buffer += unit_text; + + buffer += "\n$BeginDESCRIPTION\n"; + + EDA_RECT bbbox = m_board->ComputeBoundingBox(); + + buffer += "\n$BOARD\n"; + + sprintf( line, "upper_left_corner %9.6f %9.6f\n", + bbbox.GetX() * conv_unit, bbbox.GetY() * conv_unit ); + buffer += line; + + sprintf( line, "lower_right_corner %9.6f %9.6f\n", + bbbox.GetRight() * conv_unit, bbbox.GetBottom() * conv_unit ); + buffer += line; + + buffer += "$EndBOARD\n\n"; + + for( auto Module : m_board->Modules() ) + { + sprintf( line, "$MODULE %s\n", EscapedUTF8( Module->GetReference() ).c_str() ); + buffer += line; + + sprintf( line, "reference %s\n", EscapedUTF8( Module->GetReference() ).c_str() ); + sprintf( line, "value %s\n", EscapedUTF8( Module->GetValue() ).c_str() ); + sprintf( line, "footprint %s\n", + EscapedUTF8( FROM_UTF8( Module->GetFPID().Format().c_str() ) ).c_str() ); + buffer += line; + + buffer += "attribut"; + + if( Module->GetAttributes() & MOD_VIRTUAL ) + buffer += " virtual"; + + if( Module->GetAttributes() & MOD_CMS ) + buffer += " smd"; + + if( ( Module->GetAttributes() & (MOD_VIRTUAL | MOD_CMS) ) == 0 ) + buffer += " none"; + + buffer += "\n"; + + module_pos = Module->GetPosition(); + module_pos -= m_place_Offset; + + sprintf( line, "position %9.6f %9.6f orientation %.2f\n", + module_pos.x * conv_unit, module_pos.y * conv_unit, + Module->GetOrientation() / 10.0 ); + buffer += line; + + if( Module->GetLayer() == F_Cu ) + buffer += "layer front\n"; + else if( Module->GetLayer() == B_Cu ) + buffer += "layer back\n"; + else + buffer += "layer other\n"; + + for( auto pad : Module->Pads() ) + { + sprintf( line, "$PAD \"%s\"\n", TO_UTF8( pad->GetName() ) ); + buffer += line; + + int layer = 0; + + if( pad->GetLayerSet()[B_Cu] ) + layer = 1; + + if( pad->GetLayerSet()[F_Cu] ) + layer |= 2; + + static const char* layer_name[4] = { "nocopper", "back", "front", "both" }; + sprintf( line, "Shape %s Layer %s\n", TO_UTF8( pad->ShowPadShape() ), layer_name[layer] ); + buffer += line; + + sprintf( line, "position %9.6f %9.6f size %9.6f %9.6f orientation %.2f\n", + pad->GetPos0().x * conv_unit, pad->GetPos0().y * conv_unit, + pad->GetSize().x * conv_unit, pad->GetSize().y * conv_unit, + (pad->GetOrientation() - Module->GetOrientation()) / 10.0 ); + buffer += line; + + sprintf( line, "drill %9.6f\n", pad->GetDrillSize().x * conv_unit ); + buffer += line; + + sprintf( line, "shape_offset %9.6f %9.6f\n", + pad->GetOffset().x * conv_unit, pad->GetOffset().y * conv_unit ); + buffer += line; + + buffer += "$EndPAD\n"; + } + + sprintf( line, "$EndMODULE %s\n\n", TO_UTF8 (Module->GetReference() ) ); + buffer += line; + } + + // Generate EOF. + buffer += "$EndDESCRIPTION\n"; + + return buffer; +} diff --git a/pcbnew/exporters/export_footprints_placefile.h b/pcbnew/exporters/export_footprints_placefile.h new file mode 100644 index 0000000000..cefc1497b5 --- /dev/null +++ b/pcbnew/exporters/export_footprints_placefile.h @@ -0,0 +1,117 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015-2019 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef EXPORT_FOOTPRINTS_PLACEFILE_H +#define EXPORT_FOOTPRINTS_PLACEFILE_H + + +#include +#include + +/** + * The ASCII format of the kicad place file is: + * ### Module positions - created on 04/12/2012 15:24:24 ### + * ### Printed by Pcbnew version pcbnew (2012-11-30 BZR 3828)-testing + * ## Unit = inches, Angle = deg. + * or + * ## Unit = mm, Angle = deg. + * ## Side : top + * or + * ## Side : bottom + * or + * ## Side : all + * # Ref Val Package PosX PosY Rot Side + * C123 0,1uF/50V SM0603 1.6024 -2.6280 180.0 Front + * C124 0,1uF/50V SM0603 1.6063 -2.7579 180.0 Front + * C125 0,1uF/50V SM0603 1.6010 -2.8310 180.0 Front + * ## End + */ + + +class PLACE_FILE_EXPORTER +{ +public: + + /** Create a PLACE_FILE_EXPORTER + * @param aBoard is the board + * @param aUnitsMM is the unit option: true foo mm, false for inches + * @param aForceSmdItems true to force not virtual and not flagged smd footprints + * but having only smd pads to be in list + * @param aTopSide true to generate top side info + * @param aBottomSide true to generate bottom side info + * @param aFormatCSV true to generate a csv format info, false to generate a ascii info + */ + PLACE_FILE_EXPORTER( BOARD* aBoard, bool aUnitsMM, + bool aForceSmdItems, bool aTopSide, bool aBottomSide, bool aFormatCSV ); + + /** + * build a string filled with the position data + */ + std::string GenPositionData(); + + /** + * build a string filled with the pad report data + * This report does not used options aForceSmdItems,aTopSide, aBottomSide + * and aFormatCSV. + * All footprints and their pads on board are reported. + */ + std::string GenReportData(); + + /** @return the footprint count found on board by GenPositionData() + * must be called only after GenPositionData() is run + */ + int GetFootprintCount() { return m_fpCount; } + + /** + * @return the list of not virtual footprints with MOD_CMS flag not set + * but having only smd pads. + * This list can be used to force this flag. + * it is filled only if forceSmdItems is true + */ + std::vector& GetSmdFootprintsNotLabeledSMD() + { + return m_smdFootprintsNotLabeledSMD; + } + + // Use standard board side name. do not translate them, + // they are keywords in place file + static std::string GetFrontSideName() { return std::string( "top" ); } + static std::string GetBackSideName() { return std::string( "bottom" ); } + +private: + BOARD* m_board; + bool m_unitsMM; // true for mm, false for inches + bool m_forceSmdItems; // If true, non virtual fp with the flag MOD_CMD not set but + // having only smd pads will be in list + // and will be added in m_smdFootprintsNotLabeledSMD + int m_side; // PCB_BACK_SIDE, PCB_FRONT_SIDE, PCB_BOTH_SIDES + bool m_formatCSV; // true for csv format, false for ascii (utf8) format + int m_fpCount; // Number of footprints in list, for info + wxPoint m_place_Offset; // Offset for coordinates in generated data. + + // A list of footprints with MOD_CMS flag not set but having only smd pads. + // This list can be used to force this flag. + std::vector m_smdFootprintsNotLabeledSMD; +}; + +#endif // #ifndef EXPORT_FOOTPRINTS_PLACEFILE_H diff --git a/pcbnew/exporters/gen_footprints_placefile.cpp b/pcbnew/exporters/gen_footprints_placefile.cpp index b59334c9b9..c5bcd66463 100644 --- a/pcbnew/exporters/gen_footprints_placefile.cpp +++ b/pcbnew/exporters/gen_footprints_placefile.cpp @@ -44,45 +44,14 @@ #include #include #include +#include -/* - * The ASCII format of the kicad place file is: - * ### Module positions - created on 04/12/2012 15:24:24 ### - * ### Printed by Pcbnew version pcbnew (2012-11-30 BZR 3828)-testing - * ## Unit = inches, Angle = deg. - * or - * ## Unit = mm, Angle = deg. - * ## Side : top - * or - * ## Side : bottom - * or - * ## Side : all - * # Ref Val Package PosX PosY Rot Side - * C123 0,1uF/50V SM0603 1.6024 -2.6280 180.0 Front - * C124 0,1uF/50V SM0603 1.6063 -2.7579 180.0 Front - * C125 0,1uF/50V SM0603 1.6010 -2.8310 180.0 Front - * ## End - */ #define PLACEFILE_UNITS_KEY wxT( "PlaceFileUnits" ) #define PLACEFILE_OPT_KEY wxT( "PlaceFileOpts" ) #define PLACEFILE_FORMAT_KEY wxT( "PlaceFileFormat" ) -#define PCB_BACK_SIDE 0 -#define PCB_FRONT_SIDE 1 -#define PCB_BOTH_SIDES 2 - -class LIST_MOD // An helper class used to build a list of useful footprints. -{ -public: - MODULE* m_Module; // Link to the actual footprint - wxString m_Reference; // Its schematic reference - wxString m_Value; // Its schematic value - LAYER_NUM m_Layer; // its side (B_Cu, or F_Cu) -}; - - /** * The dialog to create footprint position files and choose options (one or 2 files, units * and force all SMD footprints in list) @@ -100,11 +69,11 @@ public: // We use a sdbSizer to get platform-dependent ordering of the action buttons, but // that requires us to correct the button labels here. - m_sdbSizer1OK->SetLabel( _( "Generate Position File" ) ); - m_sdbSizer1Cancel->SetLabel( _( "Close" ) ); - m_sdbSizer1->Layout(); + m_sdbSizerOK->SetLabel( _( "Generate Position File" ) ); + m_sdbSizerCancel->SetLabel( _( "Close" ) ); + m_sdbSizer->Layout(); - m_sdbSizer1OK->SetDefault(); + m_sdbSizerOK->SetDefault(); GetSizer()->SetSizeHints(this); Centre(); @@ -154,10 +123,6 @@ int DIALOG_GEN_FOOTPRINT_POSITION::m_unitsOpt = 0; int DIALOG_GEN_FOOTPRINT_POSITION::m_fileOpt = 0; int DIALOG_GEN_FOOTPRINT_POSITION::m_fileFormat = 0; -// Use standard board side name. do not translate them, -// they are keywords in place file -const wxString frontSideName = wxT( "top" ); -const wxString backSideName = wxT( "bottom" ); void DIALOG_GEN_FOOTPRINT_POSITION::initDialog() { @@ -229,8 +194,6 @@ void DIALOG_GEN_FOOTPRINT_POSITION::OnGenerate( wxCommandEvent& event ) m_parent->SetPlotSettings( m_plotOpts ); CreateFiles(); - - // the dialog is not closed here. } @@ -242,19 +205,36 @@ bool DIALOG_GEN_FOOTPRINT_POSITION::CreateFiles() bool singleFile = OneFileOnly(); bool useCSVfmt = m_fileFormat == 1; int fullcount = 0; + int top_side = true; + int bottom_side = true; - // Count the footprints to place, do not yet create a file - int fpcount = m_parent->DoGenFootprintsPositionFile( wxEmptyString, UnitsMM(), - ForceAllSmd(), PCB_BOTH_SIDES, - useCSVfmt ); - if( fpcount == 0) + // Test for any footprint candidate in list, and display the list of forced footprints + // if ForceAllSmd() is true { - wxMessageBox( _( "No footprint for automated placement." ) ); - return false; + PLACE_FILE_EXPORTER exporter( brd, UnitsMM(), ForceAllSmd(), top_side, bottom_side, useCSVfmt ); + exporter.GenPositionData(); + + if( exporter.GetFootprintCount() == 0) + { + wxMessageBox( _( "No footprint for automated placement." ) ); + return false; + } + + if( ForceAllSmd() ) + { + std::vector& fp_no_smd_list = exporter.GetSmdFootprintsNotLabeledSMD(); + + for( MODULE* item : fp_no_smd_list ) + { + msg.Printf( _( "footprint %s (not set as SMD) forced in list" ), item->GetReference() ); + m_reporter->Report( msg, REPORTER::RPT_INFO ); + } + } } - // Create output directory if it does not exist (also transform it in - // absolute path). Bail if it fails + // Create output directory if it does not exist. + // Also transform it in absolute path. + // Bail if it fails wxFileName outputDir = wxFileName::DirName( m_plotOpts.GetOutputDirectory() ); wxString boardFilename = m_parent->GetBoard()->GetFileName(); @@ -271,17 +251,17 @@ bool DIALOG_GEN_FOOTPRINT_POSITION::CreateFiles() fn = m_parent->GetBoard()->GetFileName(); fn.SetPath( outputDir.GetPath() ); - // Create the the Front or Top side placement file, - // or the single file - int side = PCB_FRONT_SIDE; + // Create the the Front or Top side placement file, or a single file + top_side = true; + bottom_side = false; if( singleFile ) { - side = PCB_BOTH_SIDES; + bottom_side = true; fn.SetName( fn.GetName() + wxT( "-" ) + wxT("all") ); } else - fn.SetName( fn.GetName() + wxT( "-" ) + frontSideName ); + fn.SetName( fn.GetName() + wxT( "-" ) + PLACE_FILE_EXPORTER::GetFrontSideName().c_str() ); if( useCSVfmt ) @@ -292,21 +272,22 @@ bool DIALOG_GEN_FOOTPRINT_POSITION::CreateFiles() else fn.SetExt( FootprintPlaceFileExtension ); - fpcount = m_parent->DoGenFootprintsPositionFile( fn.GetFullPath(), UnitsMM(), - ForceAllSmd(), side, useCSVfmt ); + int fpcount = m_parent->DoGenFootprintsPositionFile( fn.GetFullPath(), UnitsMM(), + ForceAllSmd(), + top_side, bottom_side, useCSVfmt ); if( fpcount < 0 ) { - msg.Printf( _( "Unable to create \"%s\"." ), GetChars( fn.GetFullPath() ) ); + msg.Printf( _( "Unable to create \"%s\"." ), fn.GetFullPath() ); wxMessageBox( msg ); m_reporter->Report( msg, REPORTER::RPT_ERROR ); return false; } if( singleFile ) - msg.Printf( _( "Place file: \"%s\"." ), GetChars( fn.GetFullPath() ) ); + msg.Printf( _( "Place file: \"%s\"." ), fn.GetFullPath() ); else msg.Printf( _( "Front side (top side) place file: \"%s\"." ), - GetChars( fn.GetFullPath() ) ); + fn.GetFullPath() ); m_reporter->Report( msg, REPORTER::RPT_INFO ); msg.Printf( _( "Component count: %d." ), fpcount ); @@ -320,10 +301,11 @@ bool DIALOG_GEN_FOOTPRINT_POSITION::CreateFiles() // Create the Back or Bottom side placement file fullcount = fpcount; - side = PCB_BACK_SIDE; + top_side = false; + bottom_side = true; fn = brd->GetFileName(); fn.SetPath( outputDir.GetPath() ); - fn.SetName( fn.GetName() + wxT( "-" ) + backSideName ); + fn.SetName( fn.GetName() + wxT( "-" ) + PLACE_FILE_EXPORTER::GetBackSideName().c_str() ); if( useCSVfmt ) { @@ -334,11 +316,11 @@ bool DIALOG_GEN_FOOTPRINT_POSITION::CreateFiles() fn.SetExt( FootprintPlaceFileExtension ); fpcount = m_parent->DoGenFootprintsPositionFile( fn.GetFullPath(), UnitsMM(), - ForceAllSmd(), side, useCSVfmt ); + ForceAllSmd(), top_side, bottom_side, useCSVfmt ); if( fpcount < 0 ) { - msg.Printf( _( "Unable to create file \"%s\"." ), GetChars( fn.GetFullPath() ) ); + msg.Printf( _( "Unable to create file \"%s\"." ), fn.GetFullPath() ); m_reporter->Report( msg, REPORTER::RPT_ERROR ); wxMessageBox( msg ); return false; @@ -347,7 +329,7 @@ bool DIALOG_GEN_FOOTPRINT_POSITION::CreateFiles() // Display results if( !singleFile ) { - msg.Printf( _( "Back side (bottom side) place file: \"%s\"." ), GetChars( fn.GetFullPath() ) ); + msg.Printf( _( "Back side (bottom side) place file: \"%s\"." ), fn.GetFullPath() ); m_reporter->Report( msg, REPORTER::RPT_INFO ); msg.Printf( _( "Component count: %d." ), fpcount ); @@ -367,249 +349,48 @@ bool DIALOG_GEN_FOOTPRINT_POSITION::CreateFiles() return true; } -// Defined values to write coordinates using inches or mm: -static const double conv_unit_inch = 0.001 / IU_PER_MILS ; // units = INCHES -static const char unit_text_inch[] = "## Unit = inches, Angle = deg.\n"; - -static const double conv_unit_mm = 1.0 / IU_PER_MM; // units = mm -static const char unit_text_mm[] = "## Unit = mm, Angle = deg.\n"; - -static wxPoint File_Place_Offset; // Offset coordinates for generated file. - - -// Sort function use by GenereModulesPosition() -// sort is made by side (layer) top layer first -// then by reference increasing order -static bool sortFPlist( const LIST_MOD& ref, const LIST_MOD& tst ) -{ - if( ref.m_Layer == tst.m_Layer ) - return StrNumCmp( ref.m_Reference, tst.m_Reference ) < 0; - - return ref.m_Layer > tst.m_Layer; -} - - -/** - * Helper function HasNonSMDPins - * returns true if the given module has any non smd pins, such as through hole - * and therefore cannot be placed automatically. - */ -static bool HasNonSMDPins( MODULE* aModule ) -{ - for( auto pad : aModule->Pads() ) - { - if( pad->GetAttribute() != PAD_ATTRIB_SMD ) - return true; - } - - return false; -} int PCB_EDITOR_CONTROL::GeneratePosFile( const TOOL_EVENT& aEvent ) { PCB_EDIT_FRAME* editFrame = getEditFrame(); DIALOG_GEN_FOOTPRINT_POSITION dlg( editFrame ); - + dlg.ShowModal(); return 0; } -/* - * Creates a footprint position file - * aSide = 0 -> Back (bottom) side) - * aSide = 1 -> Front (top) side) - * aSide = 2 -> both sides - * if aFullFileName is empty, the file is not created, only the - * count of footprints to place is returned - */ + int PCB_EDIT_FRAME::DoGenFootprintsPositionFile( const wxString& aFullFileName, bool aUnitsMM, - bool aForceSmdItems, int aSide, bool aFormatCSV ) + bool aForceSmdItems, bool aTopSide, bool BottomSide, bool aFormatCSV ) { - // Minimal text lengths: - int lenRefText = 8; - int lenValText = 8; - int lenPkgText = 16; + FILE * file = NULL; - File_Place_Offset = GetAuxOrigin(); - - // Calculating the number of useful footprints (CMS attribute, not VIRTUAL) - int footprintCount = 0; - - // Select units: - double conv_unit = aUnitsMM ? conv_unit_mm : conv_unit_inch; - const char *unit_text = aUnitsMM ? unit_text_mm : unit_text_inch; - - // Build and sort the list of footprints alphabetically - std::vector list; - list.reserve( footprintCount ); - - for( auto footprint : GetBoard()->Modules() ) + if( !aFullFileName.IsEmpty() ) { - if( aSide != PCB_BOTH_SIDES ) - { - if( footprint->GetLayer() == B_Cu && aSide == PCB_FRONT_SIDE) - continue; - if( footprint->GetLayer() == F_Cu && aSide == PCB_BACK_SIDE) - continue; - } + file = wxFopen( aFullFileName, wxT( "wt" ) ); - if( footprint->GetAttributes() & MOD_VIRTUAL ) - { - DBG( printf( "skipping footprint %s because it's virtual\n", - TO_UTF8( footprint->GetReference() ) );) - continue; - } - - if( ( footprint->GetAttributes() & MOD_CMS ) == 0 ) - { - if( aForceSmdItems ) // true to fix a bunch of mis-labeled footprints: - { - if( !HasNonSMDPins( footprint ) ) - { - // all footprint's pins are SMD, mark the part for pick and place - footprint->SetAttributes( footprint->GetAttributes() | MOD_CMS ); - OnModify(); - } - else - { - DBG( printf( "skipping %s because it is not marked CMS and has non-SMD pins\n", - TO_UTF8( footprint->GetReference() ) ) ); - continue; - } - } - else - continue; - } - - footprintCount++; - - LIST_MOD item; - item.m_Module = footprint; - item.m_Reference = footprint->GetReference(); - item.m_Value = footprint->GetValue(); - item.m_Layer = footprint->GetLayer(); - list.push_back( item ); - - lenRefText = std::max( lenRefText, int(item.m_Reference.length()) ); - lenValText = std::max( lenValText, int(item.m_Value.length()) ); - lenPkgText = std::max( lenPkgText, int(item.m_Module->GetFPID().GetLibItemName().length()) ); + if( file == NULL ) + return -1; } - if( aFullFileName.IsEmpty() ) - return footprintCount; + std::string data; + PLACE_FILE_EXPORTER exporter( GetBoard(), aUnitsMM, aForceSmdItems, + aTopSide, BottomSide, aFormatCSV ); + data = exporter.GenPositionData(); - FILE * file = wxFopen( aFullFileName, wxT( "wt" ) ); - - if( file == NULL ) - return -1; - - if( list.size() > 1 ) - sort( list.begin(), list.end(), sortFPlist ); - - // Switch the locale to standard C (needed to print floating point numbers) - LOCALE_IO toggle; - - if( aFormatCSV ) + // if aFullFileName is empty, the file is not created, only the + // count of footprints to place is returned + if( file ) { - wxChar csv_sep = ','; - - // Set first line:; - fprintf( file, "Ref%cVal%cPackage%cPosX%cPosY%cRot%cSide\n", - csv_sep, csv_sep, csv_sep, csv_sep, csv_sep, csv_sep ); - - for( int ii = 0; ii < footprintCount; ii++ ) - { - wxPoint footprint_pos; - footprint_pos = list[ii].m_Module->GetPosition(); - footprint_pos -= File_Place_Offset; - - LAYER_NUM layer = list[ii].m_Module->GetLayer(); - wxASSERT( layer == F_Cu || layer == B_Cu ); - - wxString line = "\"" + list[ii].m_Reference; - line << "\"" << csv_sep; - line << "\"" << list[ii].m_Value; - line << "\"" << csv_sep; - line << "\"" << list[ii].m_Module->GetFPID().GetLibItemName().wx_str(); - line << "\"" << csv_sep; - - line << wxString::Format( "%f%c%f%c%f", - footprint_pos.x * conv_unit, csv_sep, - // Keep the Y axis oriented from bottom to top, - // ( change y coordinate sign ) - -footprint_pos.y * conv_unit, csv_sep, - list[ii].m_Module->GetOrientation() / 10.0 ); - line << csv_sep; - - line << ( (layer == F_Cu ) ? frontSideName : backSideName ); - line << '\n'; - - fputs( TO_UTF8( line ), file ); - } - } - else - { - // Write file header - fprintf( file, "### Module positions - created on %s ###\n", TO_UTF8( DateAndTime() ) ); - - wxString Title = Pgm().App().GetAppName() + wxT( " " ) + GetBuildVersion(); - fprintf( file, "### Printed by Pcbnew version %s\n", TO_UTF8( Title ) ); - - fputs( unit_text, file ); - - fputs( "## Side : ", file ); - - if( aSide == PCB_BACK_SIDE ) - fputs( TO_UTF8( backSideName ), file ); - else if( aSide == PCB_FRONT_SIDE ) - fputs( TO_UTF8( frontSideName ), file ); - else - fputs( "All", file ); - - fputs( "\n", file ); - - fprintf(file, "%-*s %-*s %-*s %9.9s %9.9s %8.8s %s\n", - int(lenRefText), "# Ref", - int(lenValText), "Val", - int(lenPkgText), "Package", - "PosX", "PosY", "Rot", "Side" ); - - for( int ii = 0; ii < footprintCount; ii++ ) - { - wxPoint footprint_pos; - footprint_pos = list[ii].m_Module->GetPosition(); - footprint_pos -= File_Place_Offset; - - LAYER_NUM layer = list[ii].m_Module->GetLayer(); - wxASSERT( layer == F_Cu || layer == B_Cu ); - - if( layer == B_Cu ) - footprint_pos.x = - footprint_pos.x; - - wxString ref = list[ii].m_Reference; - wxString val = list[ii].m_Value; - wxString pkg = list[ii].m_Module->GetFPID().GetLibItemName(); - ref.Replace( wxT( " " ), wxT( "_" ) ); - val.Replace( wxT( " " ), wxT( "_" ) ); - pkg.Replace( wxT( " " ), wxT( "_" ) ); - fprintf(file, "%-*s %-*s %-*s %9.4f %9.4f %8.4f %s\n", - lenRefText, TO_UTF8( ref ), - lenValText, TO_UTF8( val ), - lenPkgText, TO_UTF8( pkg ), - footprint_pos.x * conv_unit, - // Keep the coordinates in the first quadrant, - // (i.e. change y sign - -footprint_pos.y * conv_unit, - list[ii].m_Module->GetOrientation() / 10.0, - (layer == F_Cu ) ? TO_UTF8( frontSideName ) : TO_UTF8( backSideName )); - } - - // Write EOF - fputs( "## End\n", file ); + // Creates a footprint position file + // aSide = 0 -> Back (bottom) side) + // aSide = 1 -> Front (top) side) + // aSide = 2 -> both sides + fputs( data.c_str(), file ); + fclose( file ); } - fclose( file ); - return footprintCount; + return exporter.GetFootprintCount(); } @@ -633,14 +414,13 @@ void PCB_EDIT_FRAME::GenFootprintsReport( wxCommandEvent& event ) wxString msg; if( success ) { - msg.Printf( _( "Footprint report file created:\n\"%s\"" ), - GetChars( fn.GetFullPath() ) ); + msg.Printf( _( "Footprint report file created:\n\"%s\"" ), fn.GetFullPath() ); wxMessageBox( msg, _( "Footprint Report" ), wxICON_INFORMATION ); } else { - msg.Printf( _( "Unable to create \"%s\"" ), GetChars( fn.GetFullPath() ) ); + msg.Printf( _( "Unable to create \"%s\"" ), fn.GetFullPath() ); DisplayError( this, msg ); } } @@ -653,114 +433,16 @@ bool PCB_EDIT_FRAME::DoGenFootprintsReport( const wxString& aFullFilename, bool FILE* rptfile; wxPoint module_pos; - File_Place_Offset = wxPoint( 0, 0 ); - rptfile = wxFopen( aFullFilename, wxT( "wt" ) ); if( rptfile == NULL ) return false; - // Select units: - double conv_unit = aUnitsMM ? conv_unit_mm : conv_unit_inch; - const char *unit_text = aUnitsMM ? unit_text_mm : unit_text_inch; + std::string data; + PLACE_FILE_EXPORTER exporter ( GetBoard(), aUnitsMM, false, true, true, false ); + data = exporter.GenReportData(); - LOCALE_IO toggle; - - // Generate header file comments.) - fprintf( rptfile, "## Footprint report - date %s\n", TO_UTF8( DateAndTime() ) ); - - wxString Title = Pgm().App().GetAppName() + wxT( " " ) + GetBuildVersion(); - fprintf( rptfile, "## Created by Pcbnew version %s\n", TO_UTF8( Title ) ); - fputs( unit_text, rptfile ); - - fputs( "\n$BeginDESCRIPTION\n", rptfile ); - - EDA_RECT bbbox = GetBoard()->ComputeBoundingBox(); - - fputs( "\n$BOARD\n", rptfile ); - - fprintf( rptfile, "upper_left_corner %9.6f %9.6f\n", - bbbox.GetX() * conv_unit, - bbbox.GetY() * conv_unit ); - - fprintf( rptfile, "lower_right_corner %9.6f %9.6f\n", - bbbox.GetRight() * conv_unit, - bbbox.GetBottom() * conv_unit ); - - fputs( "$EndBOARD\n\n", rptfile ); - - for( auto Module : GetBoard()->Modules() ) - { - fprintf( rptfile, "$MODULE %s\n", EscapedUTF8( Module->GetReference() ).c_str() ); - - fprintf( rptfile, "reference %s\n", EscapedUTF8( Module->GetReference() ).c_str() ); - fprintf( rptfile, "value %s\n", EscapedUTF8( Module->GetValue() ).c_str() ); - fprintf( rptfile, "footprint %s\n", - EscapedUTF8( FROM_UTF8( Module->GetFPID().Format().c_str() ) ).c_str() ); - - msg = wxT( "attribut" ); - - if( Module->GetAttributes() & MOD_VIRTUAL ) - msg += wxT( " virtual" ); - - if( Module->GetAttributes() & MOD_CMS ) - msg += wxT( " smd" ); - - if( ( Module->GetAttributes() & (MOD_VIRTUAL | MOD_CMS) ) == 0 ) - msg += wxT( " none" ); - - msg += wxT( "\n" ); - fputs( TO_UTF8( msg ), rptfile ); - - module_pos = Module->GetPosition(); - module_pos.x -= File_Place_Offset.x; - module_pos.y -= File_Place_Offset.y; - - fprintf( rptfile, "position %9.6f %9.6f orientation %.2f\n", - module_pos.x * conv_unit, - module_pos.y * conv_unit, - Module->GetOrientation() / 10.0 ); - - if( Module->GetLayer() == F_Cu ) - fputs( "layer front\n", rptfile ); - else if( Module->GetLayer() == B_Cu ) - fputs( "layer back\n", rptfile ); - else - fputs( "layer other\n", rptfile ); - - for( auto pad : Module->Pads() ) - { - fprintf( rptfile, "$PAD \"%s\"\n", TO_UTF8( pad->GetName() ) ); - int layer = 0; - - if( pad->GetLayerSet()[B_Cu] ) - layer = 1; - - if( pad->GetLayerSet()[F_Cu] ) - layer |= 2; - - static const char* layer_name[4] = { "nocopper", "back", "front", "both" }; - fprintf( rptfile, "Shape %s Layer %s\n", TO_UTF8( pad->ShowPadShape() ), layer_name[layer] ); - - fprintf( rptfile, "position %9.6f %9.6f size %9.6f %9.6f orientation %.2f\n", - pad->GetPos0().x * conv_unit, pad->GetPos0().y * conv_unit, - pad->GetSize().x * conv_unit, pad->GetSize().y * conv_unit, - (pad->GetOrientation() - Module->GetOrientation()) / 10.0 ); - - fprintf( rptfile, "drill %9.6f\n", pad->GetDrillSize().x * conv_unit ); - - fprintf( rptfile, "shape_offset %9.6f %9.6f\n", - pad->GetOffset().x * conv_unit, - pad->GetOffset().y * conv_unit ); - - fprintf( rptfile, "$EndPAD\n" ); - } - - fprintf( rptfile, "$EndMODULE %s\n\n", TO_UTF8 (Module->GetReference() ) ); - } - - // Generate EOF. - fputs( "$EndDESCRIPTION\n", rptfile ); + fputs( data.c_str(), rptfile ); fclose( rptfile ); return true; diff --git a/pcbnew/pcb_edit_frame.h b/pcbnew/pcb_edit_frame.h index 8b280a38db..705f5f7f4c 100644 --- a/pcbnew/pcb_edit_frame.h +++ b/pcbnew/pcb_edit_frame.h @@ -564,15 +564,15 @@ public: * @param aUnitsMM = false to use inches, true to use mm in coordinates * @param aForceSmdItems = true to force all footprints with smd pads in list * = false to put only footprints with option "INSERT" in list - * @param aSide = 0 to list footprints on BACK side, - * 1 to list footprints on FRONT side - * 2 to list footprints on both sides + * @param aTopSide true to list footprints on front (top) side, + * @param BottomSide true to list footprints on back (bottom) side, + * if aTopSide and aTopSide are true, list footprints on both sides * @param aFormatCSV = true to use a comma separated file (CSV) format; defautl = false * @return the number of footprints found on aSide side, * or -1 if the file could not be created */ int DoGenFootprintsPositionFile( const wxString& aFullFileName, bool aUnitsMM, - bool aForceSmdItems, int aSide, bool aFormatCSV = false ); + bool aForceSmdItems, bool aTopSide, bool BottomSide, bool aFormatCSV = false ); /** * Function GenFootprintsReport @@ -1008,7 +1008,7 @@ public: void UpdateTitle(); void On3DShapeLibWizard( wxCommandEvent& event ); - + /** * Allows Pcbnew to install its preferences panel into the preferences dialog. */