Pcbnew: Add experimental place file (P&P) export in Gerber format.
This commit is contained in:
parent
6084614371
commit
36253450e7
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
||||
}
|
||||
|
|
|
@ -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">"ASCII" "CSV"</property>
|
||||
<property name="choices">"ASCII" "CSV" "Gerber (very experimental)"</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>
|
||||
|
|
|
@ -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();
|
||||
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue