Pcbnew: Add experimental place file (P&P) export in Gerber format.

This commit is contained in:
jean-pierre charras 2019-09-15 17:08:57 +02:00
parent 6084614371
commit 36253450e7
10 changed files with 669 additions and 28 deletions

View File

@ -245,9 +245,42 @@ std::string GBR_APERTURE_METADATA::FormatAttribute( GBR_APERTURE_ATTRIB aAttribu
attribute_string = "TA.AperFunction,ComponentMain";
break;
case GBR_APERTURE_ATTRIB_CMP_COURTYARD: // print info associated to a component
// print the component courtyard polygon
case GBR_APERTURE_ATTRIB_PAD1_POSITION: // print info associated to a component
// flashed shape at pad 1 position
// (pad 1 is also pad A1 or pad AA1)
// in placement files
attribute_string = "TA.AperFunction,ComponentPin";
break;
case GBR_APERTURE_ATTRIB_PADOTHER_POSITION: // print info associated to a component
// flashed shape at pads position (all but pad 1)
// in placement files
// Currently: (could be changed later) same as
// GBR_APERTURE_ATTRIB_PADOTHER_POSITION
attribute_string = "TA.AperFunction,ComponentPin";
break;
case GBR_APERTURE_ATTRIB_CMP_BODY: // print info associated to a component
// print the component physical body
// polygon in placement files
attribute_string = "TA.AperFunction,ComponentOutline,Body";
break;
case GBR_APERTURE_ATTRIB_CMP_LEAD2LEAD: // print info associated to a component
// print the component physical lead to lead
// polygon in placement files
attribute_string = "TA.AperFunction,ComponentOutline,Lead2Lead";
break;
case GBR_APERTURE_ATTRIB_CMP_FOOTPRINT: // print info associated to a component
// print the component footprint bounding box
// polygon in placement files
attribute_string = "TA.AperFunction,ComponentOutline,Footprint";
break;
case GBR_APERTURE_ATTRIB_CMP_COURTYARD: // print info associated to a component
// print the component courtyard
// polygon in placement files
attribute_string = "TA.AperFunction,ComponentOutline,Courtyard";
break;
@ -512,3 +545,53 @@ bool FormatNetAttribute( std::string& aPrintedText, std::string& aLastNetAttribu
return true;
}
/************ class GBR_CMP_PNP_METADATA *************/
void GBR_CMP_PNP_METADATA::ClearData()
{
// Clear all strings
m_Orientation = 0.0;
m_Manufacturer.Clear();
m_MPN.Clear();
m_Package.Clear();
m_Value.Clear();
m_MountType = MOUNT_TYPE_UNSPECIFIED;
}
/**
* @return a string containing the formated metadata in X2 syntax.
* one line by non empty data
* the orientation is always generated
*/
wxString GBR_CMP_PNP_METADATA::FormatCmpPnPMetadata()
{
wxString text;
wxString start_of_line( "%TO.");
wxString end_of_line( "*%\n" );
wxString mounType[] =
{
"Other", "SMD", "BGA", "TH"
};
if( !m_Manufacturer.IsEmpty() )
text << start_of_line << "CMfr," << m_Manufacturer << end_of_line;
if( !m_MPN.IsEmpty() )
text << start_of_line << "CMPN," << m_MPN << end_of_line;
if( !m_Package.IsEmpty() )
text << start_of_line << "Cpkg," << m_Package << end_of_line;
if( !m_Footprint.IsEmpty() )
text << start_of_line << "CFtp," << m_Footprint << end_of_line;
if( !m_Value.IsEmpty() )
text << start_of_line << "CVal," << m_Value << end_of_line;
text << start_of_line << "CMnt," << mounType[m_MountType] << end_of_line;
text << start_of_line << "CRot," << m_Orientation << end_of_line;
return text;
}

View File

@ -94,9 +94,17 @@ public:
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_CMP_DRILL, ///< aperture used for pad holes in drill files
GBR_APERTURE_ATTRIB_CMP_DRILL, ///< aperture used for pad holes in drill files
GBR_APERTURE_ATTRIB_CMP_OBLONG_DRILL, ///< aperture used for pads oblong holes in drill files
GBR_APERTURE_ATTRIB_CMP_POSITION, ///< aperture used for flashed shape in placement files
GBR_APERTURE_ATTRIB_CMP_POSITION, ///< aperture used for flashed cmp position in placement files
GBR_APERTURE_ATTRIB_PAD1_POSITION, ///< aperture used for flashed pin 1 (or A1 or AA1) position in placement files
GBR_APERTURE_ATTRIB_PADOTHER_POSITION, ///< aperture used for flashed pads position in placement files
GBR_APERTURE_ATTRIB_CMP_BODY, ///< aperture used to draw component physical body outline
///< (without pins) in placement files
GBR_APERTURE_ATTRIB_CMP_LEAD2LEAD, ///< aperture used to draw component physical body outline
///< (with pins) in placement files
GBR_APERTURE_ATTRIB_CMP_FOOTPRINT, ///< aperture used to draw component footprint bounding box
///< in placement files
GBR_APERTURE_ATTRIB_CMP_COURTYARD, ///< aperture used to draw component outline courtyard
///< in placement files
GBR_APERTURE_ATTRIB_END ///< sentinel: max value

View File

@ -25,9 +25,62 @@
#ifndef GBR_NETLIST_METADATA_H
#define GBR_NETLIST_METADATA_H
/** this class handle info which can be added in a gerber P&P file as attribute
* of a component
* Only applicable to objects having the TA.AperFunction attribute "ComponentMain"
* There are specific attributes defined attached to the component by the %TO command
* %TO.CRot,<angle> The rotation angle of the component.
* The rotation angle is consistent with the one for graphics objects.
* Positive rotation is counter- clockwise as viewed from the top side, even if
* the component is on the board side.
* The base orientation of component no rotation - on the top side is as in IPC-7351.
* Components on the bottom side are of course mirrored.
* The base orientation on the bottom side is the one on the top side
* mirrored around the X axis.
* %TO.CMfr,<string> Manufacturer
* %TO.CMPN,<string> Manufacturer part number
* %TO.Cpkg,<string> Package, as per IPC-7351
* %TO.CFtp,<string> Footprint name, a string. E.g. LQFP-100_14x14mm_P0.5mm
This is the footprint name coming from the CAD tool libraries.
* %TO.CVal,<string> Value, a string. E.g. 220nF
* %TO.CMnt,<string> Mount type: (SMD|TH|Other)
* %TO.CHgt,<string> Height, a decimal, in the unit of the file.
*/
class GBR_CMP_PNP_METADATA
{
public:
enum MOUNT_TYPE
{
MOUNT_TYPE_UNSPECIFIED,
MOUNT_TYPE_SMD,
MOUNT_TYPE_BGA,
MOUNT_TYPE_TH
};
double m_Orientation; // orientation in degree
wxString m_Manufacturer; // Manufacturer name
wxString m_MPN; // Manufacturer part number
wxString m_Package; // Package, as per IPC-7351
wxString m_Footprint; // Footprint name, from library
wxString m_Value; // Component value
MOUNT_TYPE m_MountType; // SMD|TH|Other
GBR_CMP_PNP_METADATA() :
m_Orientation( 0.0 ), m_MountType( MOUNT_TYPE_UNSPECIFIED )
{}
void ClearData(); // Clear all strings
/**
* @return a string containing the formated metadata in X2 syntax.
* one line by non empty data
* the orientation is always generated
*/
wxString FormatCmpPnPMetadata();
};
/** this class handle info which can be added in a gerber file as attribute
* of an obtect
* of an object
* the GBR_INFO_TYPE types can be OR'ed to add 2 (or more) attributes
* There are only 3 net attributes defined attached to an object by the %TO command
* %TO.P

View File

@ -219,6 +219,7 @@ set( PCBNEW_EXPORTERS
exporters/gendrill_file_writer_base.cpp
exporters/gendrill_gerber_writer.cpp
exporters/gerber_jobfile_writer.cpp
exporters/gerber_placefile_writer.cpp
)
set( PCBNEW_MICROWAVE_SRCS

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Dec 1 2018)
// C++ code generated with wxFormBuilder (version Jul 10 2019)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -47,21 +47,17 @@ DIALOG_GEN_FOOTPRINT_POSITION_BASE::DIALOG_GEN_FOOTPRINT_POSITION_BASE( wxWindow
wxBoxSizer* bSizerMiddle;
bSizerMiddle = new wxBoxSizer( wxHORIZONTAL );
wxString m_rbFormatChoices[] = { _("ASCII"), _("CSV") };
wxString m_rbFormatChoices[] = { _("ASCII"), _("CSV"), _("Gerber (very experimental)") };
int m_rbFormatNChoices = sizeof( m_rbFormatChoices ) / sizeof( wxString );
m_rbFormat = new wxRadioBox( this, wxID_ANY, _("Format"), wxDefaultPosition, wxDefaultSize, m_rbFormatNChoices, m_rbFormatChoices, 1, wxRA_SPECIFY_COLS );
m_rbFormat->SetSelection( 0 );
m_rbFormat->SetMinSize( wxSize( 90,-1 ) );
bSizerMiddle->Add( m_rbFormat, 1, wxALL, 5 );
bSizerMiddle->Add( m_rbFormat, 0, wxALL, 5 );
wxString m_radioBoxUnitsChoices[] = { _("Inches"), _("Millimeters") };
int m_radioBoxUnitsNChoices = sizeof( m_radioBoxUnitsChoices ) / sizeof( wxString );
m_radioBoxUnits = new wxRadioBox( this, wxID_ANY, _("Units"), wxDefaultPosition, wxDefaultSize, m_radioBoxUnitsNChoices, m_radioBoxUnitsChoices, 1, wxRA_SPECIFY_COLS );
m_radioBoxUnits->SetSelection( 0 );
m_radioBoxUnits->SetMinSize( wxSize( 90,-1 ) );
bSizerMiddle->Add( m_radioBoxUnits, 1, wxALL, 5 );
bSizerMiddle->Add( m_radioBoxUnits, 0, wxALL, 5 );
wxString m_radioBoxFilesCountChoices[] = { _("Separate files for front and back"), _("Single file for board") };
int m_radioBoxFilesCountNChoices = sizeof( m_radioBoxFilesCountChoices ) / sizeof( wxString );
@ -69,10 +65,10 @@ 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") );
bSizerMiddle->Add( m_radioBoxFilesCount, 2, wxALL, 5 );
bSizerMiddle->Add( m_radioBoxFilesCount, 0, wxALL, 5 );
m_MainSizer->Add( bSizerMiddle, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 );
m_MainSizer->Add( bSizerMiddle, 0, wxTOP|wxRIGHT|wxLEFT|wxEXPAND, 5 );
wxBoxSizer* bSizerLower;
bSizerLower = new wxBoxSizer( wxVERTICAL );
@ -105,6 +101,9 @@ DIALOG_GEN_FOOTPRINT_POSITION_BASE::DIALOG_GEN_FOOTPRINT_POSITION_BASE( wxWindow
// Connect Events
m_browseButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GEN_FOOTPRINT_POSITION_BASE::OnOutputDirectoryBrowseClicked ), NULL, this );
m_rbFormat->Connect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_GEN_FOOTPRINT_POSITION_BASE::onSelectFormat ), NULL, this );
m_radioBoxUnits->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_GEN_FOOTPRINT_POSITION_BASE::onUpdateUIUnits ), NULL, this );
m_radioBoxFilesCount->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_GEN_FOOTPRINT_POSITION_BASE::onUpdateUIFileOpt ), NULL, this );
m_sdbSizerOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GEN_FOOTPRINT_POSITION_BASE::OnGenerate ), NULL, this );
}
@ -112,6 +111,9 @@ 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_rbFormat->Disconnect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_GEN_FOOTPRINT_POSITION_BASE::onSelectFormat ), NULL, this );
m_radioBoxUnits->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_GEN_FOOTPRINT_POSITION_BASE::onUpdateUIUnits ), NULL, this );
m_radioBoxFilesCount->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_GEN_FOOTPRINT_POSITION_BASE::onUpdateUIFileOpt ), NULL, this );
m_sdbSizerOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GEN_FOOTPRINT_POSITION_BASE::OnGenerate ), NULL, this );
}

View File

@ -14,6 +14,7 @@
<property name="file">dialog_gen_footprint_position_file_base</property>
<property name="first_id">1000</property>
<property name="help_provider">none</property>
<property name="image_path_wrapper_function_name"></property>
<property name="indent_with_spaces"></property>
<property name="internationalize">1</property>
<property name="name">dialog_gen_footprint_positions_base</property>
@ -25,6 +26,7 @@
<property name="skip_php_events">1</property>
<property name="skip_python_events">1</property>
<property name="ui_table">UI</property>
<property name="use_array_enum">0</property>
<property name="use_enum">1</property>
<property name="use_microsoft_bom">0</property>
<object class="Dialog" expanded="1">
@ -45,7 +47,7 @@
<property name="minimum_size">-1,-1</property>
<property name="name">DIALOG_GEN_FOOTPRINT_POSITION_BASE</property>
<property name="pos"></property>
<property name="size">506,476</property>
<property name="size">520,450</property>
<property name="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</property>
<property name="subclass">DIALOG_SHIM; dialog_shim.h</property>
<property name="title">Generate Footprint Position Files</property>
@ -280,7 +282,7 @@
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxTOP|wxRIGHT|wxLEFT</property>
<property name="flag">wxTOP|wxRIGHT|wxLEFT|wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
@ -290,7 +292,7 @@
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">1</property>
<property name="proportion">0</property>
<object class="wxRadioBox" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
@ -305,7 +307,7 @@
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="choices">&quot;ASCII&quot; &quot;CSV&quot;</property>
<property name="choices">&quot;ASCII&quot; &quot;CSV&quot; &quot;Gerber (very experimental)&quot;</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
@ -327,7 +329,7 @@
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size">90,-1</property>
<property name="minimum_size">-1,-1</property>
<property name="moveable">1</property>
<property name="name">m_rbFormat</property>
<property name="pane_border">1</property>
@ -351,12 +353,13 @@
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnRadioBox">onSelectFormat</event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">1</property>
<property name="proportion">0</property>
<object class="wxRadioBox" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
@ -393,7 +396,7 @@
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size">90,-1</property>
<property name="minimum_size">-1,-1</property>
<property name="moveable">1</property>
<property name="name">m_radioBoxUnits</property>
<property name="pane_border">1</property>
@ -417,12 +420,13 @@
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnUpdateUI">onUpdateUIUnits</event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">2</property>
<property name="proportion">0</property>
<object class="wxRadioBox" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
@ -483,6 +487,7 @@
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnUpdateUI">onUpdateUIFileOpt</event>
</object>
</object>
</object>

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Dec 1 2018)
// C++ code generated with wxFormBuilder (version Jul 10 2019)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -56,12 +56,15 @@ class DIALOG_GEN_FOOTPRINT_POSITION_BASE : public DIALOG_SHIM
// Virtual event handlers, overide them in your derived class
virtual void OnOutputDirectoryBrowseClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void onSelectFormat( wxCommandEvent& event ) { event.Skip(); }
virtual void onUpdateUIUnits( wxUpdateUIEvent& event ) { event.Skip(); }
virtual void onUpdateUIFileOpt( wxUpdateUIEvent& event ) { event.Skip(); }
virtual void OnGenerate( wxCommandEvent& event ) { event.Skip(); }
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( 506,476 ), 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( 520,450 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
~DIALOG_GEN_FOOTPRINT_POSITION_BASE();
};

View File

@ -45,6 +45,7 @@
#include <wx_html_report_panel.h>
#include <dialog_gen_footprint_position_file_base.h>
#include <export_footprints_placefile.h>
#include "gerber_placefile_writer.h"
#define PLACEFILE_UNITS_KEY wxT( "PlaceFileUnits" )
@ -93,7 +94,23 @@ private:
void OnOutputDirectoryBrowseClicked( wxCommandEvent& event ) override;
void OnGenerate( wxCommandEvent& event ) override;
bool CreateFiles();
void onUpdateUIUnits( wxUpdateUIEvent& event ) override
{
m_radioBoxUnits->Enable( m_rbFormat->GetSelection() != 2 );
}
void onUpdateUIFileOpt( wxUpdateUIEvent& event ) override
{
m_radioBoxFilesCount->Enable( m_rbFormat->GetSelection() != 2 );
}
/** Creates files in text or csv format
*/
bool CreateAsciiFiles();
/** Creates placement files in gerber format
*/
bool CreateGerberFiles();
// accessors to options:
wxString GetOutputDirectory()
@ -135,6 +152,8 @@ void DIALOG_GEN_FOOTPRINT_POSITION::initDialog()
// Output directory
m_outputDirectoryName->SetValue( m_plotOpts.GetOutputDirectory() );
// Update Options
m_radioBoxUnits->SetSelection( m_unitsOpt );
m_radioBoxFilesCount->SetSelection( m_fileOpt );
m_rbFormat->SetSelection( m_fileFormat );
@ -193,11 +212,95 @@ void DIALOG_GEN_FOOTPRINT_POSITION::OnGenerate( wxCommandEvent& event )
m_plotOpts.SetOutputDirectory( dirStr );
m_parent->SetPlotSettings( m_plotOpts );
CreateFiles();
if( m_fileFormat == 2 )
CreateGerberFiles();
else
CreateAsciiFiles();
}
bool DIALOG_GEN_FOOTPRINT_POSITION::CreateGerberFiles()
{
BOARD* brd = m_parent->GetBoard();
wxFileName fn;
wxString msg;
int fullcount = 0;
// 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();
m_reporter = &m_messagesPanel->Reporter();
if( !EnsureFileDirectoryExists( &outputDir, boardFilename, m_reporter ) )
{
msg.Printf( _( "Could not write plot files to folder \"%s\"." ),
outputDir.GetPath() );
DisplayError( this, msg );
return false;
}
fn = m_parent->GetBoard()->GetFileName();
fn.SetPath( outputDir.GetPath() );
// Create the the Front or Top side placement file, or a single file
// Test for any footprint candidate in list, and display the list of forced footprints
// if ForceAllSmd() is true
PLACEFILE_GERBER_WRITER exporter( brd );
wxString filename = exporter.GetPlaceFileName( fn.GetFullPath(), F_Cu );
int fpcount = exporter.CreatePlaceFile( filename, F_Cu );
if( fpcount < 0 )
{
msg.Printf( _( "Unable to create \"%s\"." ), fn.GetFullPath() );
wxMessageBox( msg );
m_reporter->Report( msg, REPORTER::RPT_ERROR );
return false;
}
msg.Printf( _( "Front side (top side) place file: \"%s\"." ), filename );
m_reporter->Report( msg, REPORTER::RPT_INFO );
msg.Printf( _( "Component count: %d." ), fpcount );
m_reporter->Report( msg, REPORTER::RPT_INFO );
// Create the Back or Bottom side placement file
fullcount = fpcount;
filename = exporter.GetPlaceFileName( fn.GetFullPath(), B_Cu );
fpcount = exporter.CreatePlaceFile( filename, B_Cu );
if( fpcount < 0 )
{
msg.Printf( _( "Unable to create file \"%s\"." ), filename );
m_reporter->Report( msg, REPORTER::RPT_ERROR );
wxMessageBox( msg );
return false;
}
// Display results
msg.Printf( _( "Back side (bottom side) place file: \"%s\"." ), filename );
m_reporter->Report( msg, REPORTER::RPT_INFO );
msg.Printf( _( "Component count: %d." ), fpcount );
m_reporter->Report( msg, REPORTER::RPT_INFO );
fullcount += fpcount;
msg.Printf( _( "Full component count: %d\n" ), fullcount );
m_reporter->Report( msg, REPORTER::RPT_INFO );
m_reporter->Report( _( "Component Placement File generation OK." ), REPORTER::RPT_ACTION );
return true;
}
bool DIALOG_GEN_FOOTPRINT_POSITION::CreateFiles()
bool DIALOG_GEN_FOOTPRINT_POSITION::CreateAsciiFiles()
{
BOARD * brd = m_parent->GetBoard();
wxFileName fn;

View File

@ -0,0 +1,281 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 Jean_Pierre Charras <jp.charras at wanadoo.fr>
* Copyright (C) 1992-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 3 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, see <http://www.gnu.org/licenses/>.
*/
/**
* @file gerber_placefile_writer.cpp
* @brief Functions to create place files in gerber X2 format.
*/
#include <fctsys.h>
#include "gerber_placefile_writer.h"
#include <vector>
#include <plotter.h>
#include <kicad_string.h>
#include <pcb_edit_frame.h>
#include <pgm_base.h>
#include <build_version.h>
#include <class_board.h>
#include <pcbplot.h>
#include <pcbnew.h>
#include <wildcards_and_files_ext.h>
#include <reporter.h>
#include <gbr_metadata.h>
#include <class_module.h>
#include <pcbplot.h>
PLACEFILE_GERBER_WRITER::PLACEFILE_GERBER_WRITER( BOARD* aPcb )
{
m_pcb = aPcb;
/* Set conversion scale depending on drill file units */
m_conversionUnits = 1.0 / IU_PER_MM; // Gerber units = mm
m_forceSmdItems = false;
m_plotPad1Marker = true; // Place a marker to pin 1 (or A1) position
m_plotOtherPadsMarker = false; // Place a marker to other pins position
}
int PLACEFILE_GERBER_WRITER::CreatePlaceFile( wxString& aFullFilename,
PCB_LAYER_ID aLayer )
{
m_layer = aLayer;
// Collect footprints on the right layer
std::vector<MODULE*> fp_list;
for( MODULE* footprint : m_pcb->Modules() )
{
if( footprint->GetAttributes() & MOD_VIRTUAL )
continue;
if( footprint->GetLayer() == aLayer )
fp_list.push_back( footprint );
}
LOCALE_IO dummy_io; // Use the standard notation for double numbers
GERBER_PLOTTER plotter;
// Gerber drill file imply X2 format:
plotter.UseX2format( 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 P&P files
// %TF.FileFunction,Component,Ln,[top][bottom]*%
wxString text;
text.Printf( "%%TF.FileFunction,Component,L%d,%s*%%",
aLayer == B_Cu ? m_pcb->GetCopperLayerCount() : 1,
aLayer == B_Cu ? "Bottom" : "Top" );
plotter.AddLineToHeader( text );
// Add file polarity (positive)
text = "%TF.FilePolarity,Positive*%";
plotter.AddLineToHeader( text );
if( !plotter.OpenFile( aFullFilename ) )
return -1;
// We need a BRDITEMS_PLOTTER to plot pads
PCB_PLOT_PARAMS plotOpts;
BRDITEMS_PLOTTER brd_plotter( &plotter, m_pcb, plotOpts );
brd_plotter.SetLayerSet( LSET( aLayer ) );
plotter.StartPlot();
int cmp_count = 0;
for( MODULE* footprint : fp_list )
{
// Manage the aperture attributes: in drill files 3 attributes can be used:
GBR_METADATA gbr_metadata;
gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CMP_POSITION );
// Add object attribute: component reference to flash (mainly usefull for users)
wxString ref = footprint->GetReference();
gbr_metadata.SetCmpReference( ref );
gbr_metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_CMP );
// Add P&P specific attributes
GBR_CMP_PNP_METADATA pnpAttrib;
// Add rotation info (rotation is CCW, in degrees):
pnpAttrib.m_Orientation = mapRotationAngle( footprint->GetOrientationDegrees() );
// Add component type info (SMD or Through Hole):
bool is_smd_mount = footprint->GetAttributes() & MOD_CMS;
// Smd footprints can have through holes (thermal vias).
// but if a footprint is not set as SMD, it will be set as SMD
// if it does not have through hole pads
if( !is_smd_mount && !footprint->HasNonSMDPins() )
is_smd_mount = true;
pnpAttrib.m_MountType = is_smd_mount ? GBR_CMP_PNP_METADATA::MOUNT_TYPE_SMD
: GBR_CMP_PNP_METADATA::MOUNT_TYPE_TH;
// Add component value info:
pnpAttrib.m_Value = FormatStringFromGerber( footprint->GetValue() );
// Add component footprint info:
wxString fp_name = FROM_UTF8( footprint->GetFPID().GetLibItemName().c_str() );
pnpAttrib.m_Footprint = FormatStringFromGerber( fp_name );
gbr_metadata.m_NetlistMetadata.SetExtraData( pnpAttrib.FormatCmpPnPMetadata() );
wxPoint flash_pos = footprint->GetPosition() + m_offset;
int flash_diam = Millimeter2iu( 0.3 ); // arbitrary but reasonable value
plotter.FlashPadCircle( flash_pos, flash_diam, FILLED, &gbr_metadata );
gbr_metadata.m_NetlistMetadata.ClearExtraData();
if( footprint->BuildPolyCourtyard() )
{
int thickness = Millimeter2iu( 0.1 ); // arbitrary but reasonable value
gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CMP_COURTYARD );
SHAPE_POLY_SET& courtyard = aLayer == B_Cu ?
footprint->GetPolyCourtyardBack():
footprint->GetPolyCourtyardFront();
for( int ii = 0; ii < courtyard.OutlineCount(); ii++ )
{
SHAPE_LINE_CHAIN poly = courtyard.Outline( ii );
poly.Move( m_offset );
plotter.PLOTTER::PlotPoly( poly, NO_FILL, thickness, &gbr_metadata );
}
}
D_PAD* pad1 = nullptr;
if( m_plotPad1Marker )
{
pad1 = findPad1( footprint );
if( pad1 )
{
gbr_metadata.SetApertureAttrib(
GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_PAD1_POSITION );
gbr_metadata.SetPadName( pad1->GetName() );
gbr_metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_PAD );
// Flashes a diamond at pad position: use a slightly bigger size than the
// round spot to be able to see these 2 shapes when drawn at the same location
int mark_size = (flash_diam*6)/5;
plotter.FlashRegularPolygon( pad1->GetPosition() + m_offset, mark_size, 4,
0.0, FILLED, &gbr_metadata );
}
}
if( m_plotOtherPadsMarker )
{
gbr_metadata.SetApertureAttrib(
GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_PADOTHER_POSITION );
gbr_metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_PAD );
for( D_PAD* pad: footprint->Pads() )
{
if( pad == pad1 ) // Already plotted
continue;
// Skip also pads not on the current layer, like pads only
// on a tech layer
if( !pad->IsOnLayer( aLayer ) )
continue;
gbr_metadata.SetPadName( pad->GetName() );
// Flashes a round, 0 sized round shape at pad position
int mark_size = Millimeter2iu( 0.1 );
plotter.FlashPadCircle( pad->GetPosition() + m_offset, mark_size,
FILLED, &gbr_metadata );
}
}
plotter.EndBlock( nullptr ); // Close all .TO attributes
cmp_count++;
}
plotter.EndPlot();
return cmp_count;
}
double PLACEFILE_GERBER_WRITER::mapRotationAngle( double aAngle )
{
// convert a kicad footprint orientation to gerber rotation, depending on the layer
// Currently, same notation as kicad
return aAngle;
}
D_PAD* PLACEFILE_GERBER_WRITER::findPad1( MODULE* aFootprint )
{
// Fint the pad "1" or pad "A1"
// this is possible only if only one pad is found
// Usefull to place a marker in this position
std::vector<D_PAD*> pad1_list;
for( D_PAD* pad : aFootprint->Pads() )
{
if( !pad->IsOnLayer( m_layer ) )
continue;
if( pad->GetName() == "1" || pad->GetName() == "A1")
pad1_list.push_back( pad );
}
if( pad1_list.size() == 1 )
return pad1_list[0];
return nullptr;
}
const wxString PLACEFILE_GERBER_WRITER::GetPlaceFileName( const wxString& aFullBaseFilename,
PCB_LAYER_ID aLayer ) const
{
// Gerber files extension is always .gbr.
// Therefore, to mark pnp files, add "-pnp" to the filename, and a layer id.
wxFileName fn = aFullBaseFilename;
wxString post_id = "-pnp_";
post_id += aLayer == B_Cu ? "bottom" : "top";
fn.SetName( fn.GetName() + post_id );
fn.SetExt( GerberFileExtension );
return fn.GetFullPath();
}

View File

@ -0,0 +1,102 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 Jean_Pierre Charras <jp.charras at wanadoo.fr>
* Copyright (C) 1992-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 3 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, see <http://www.gnu.org/licenses/>.
*/
/**
* @file gerber_placefile_writer.h
* @brief Classes used in place file generation.
*/
#ifndef PLACEFILE_GERBER_WRITER_H
#define PLACEFILE_GERBER_WRITER_H
#include <layers_id_colors_and_visibility.h>
class BOARD;
class MODULE;
class D_PAD;
/**
* PLACEFILE_GERBER_WRITER is a class mainly used to create Gerber drill files
*/
class PLACEFILE_GERBER_WRITER
{
public:
PLACEFILE_GERBER_WRITER( BOARD* aPcb );
virtual ~PLACEFILE_GERBER_WRITER()
{
}
/**
* 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;
}
/**
* Creates an pnp gerber file
* @param aFullFilename = the full filename
* @param aLayer = layer (F_Cu or B_Cu) to generate
* @return component count, or -1 if the file cannot be created
*/
int CreatePlaceFile( wxString& aFullFilename, PCB_LAYER_ID aLayer );
/**
* @return a filename which identify the drill file function.
* @param aFullBaseFilename = a full filename. it will be modified
* to add "-pnp" and set the extension
* @param aLayer = layer (F_Cu or B_Cu) to generate
*/
const wxString GetPlaceFileName( const wxString& aFullBaseFilename,
PCB_LAYER_ID aLayer ) const;
private:
BOARD* m_pcb;
/// The board layer currently used (typically F_Cu or B_Cu)
PCB_LAYER_ID m_layer;
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_forceSmdItems;
// True to plot a flashed marker shape at pad 1 position
bool m_plotPad1Marker;
// True to plot a marker shape at other pads position
// This is a flashed 0 sized round pad
bool m_plotOtherPadsMarker;
/** convert a kicad footprint orientation to gerber rotation
* both are in degrees
*/
double mapRotationAngle( double aAngle );
/** Find the pad 1 (or pad "A1") of a footprint
* Usefull to plot a marker at this position
*/
D_PAD* findPad1( MODULE* aFootprint );
};
#endif // #ifndef PLACEFILE_GERBER_WRITER_H