KiCad PCB EDA Suite
Loading...
Searching...
No Matches
gerber_placefile_writer.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2019 Jean_Pierre Charras <jp.charras at wanadoo.fr>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
25
27
28#include <vector>
29
31#include <string_utils.h>
32#include <locale_io.h>
33#include <macros.h>
34#include <pcb_shape.h>
35#include <pcbplot.h>
37#include <gbr_metadata.h>
38#include <footprint.h>
39#include <pad.h>
40
41
43{
44 m_pcb = aPcb;
45 m_plotPad1Marker = true; // Place a marker to pin 1 (or A1) position
46 m_plotOtherPadsMarker = true; // Place a marker to other pins position
47 m_layer = PCB_LAYER_ID::UNDEFINED_LAYER; // No layer set
48}
49
50
51int PLACEFILE_GERBER_WRITER::CreatePlaceFile( const wxString& aFullFilename, PCB_LAYER_ID aLayer,
52 bool aIncludeBrdEdges, bool aExcludeDNP,
53 bool aExcludeBOM )
54{
55 m_layer = aLayer;
56
57 PCB_PLOT_PARAMS plotOpts = m_pcb->GetPlotOptions();
58
59 if( plotOpts.GetUseAuxOrigin() )
60 m_offset = m_pcb->GetDesignSettings().GetAuxOrigin();
61
62 // Collect footprints on the right layer
63 std::vector<FOOTPRINT*> fp_list;
64
65 for( FOOTPRINT* footprint : m_pcb->Footprints() )
66 {
67 if( footprint->GetAttributes() & FP_EXCLUDE_FROM_POS_FILES )
68 continue;
69
70 if( aExcludeDNP && ( footprint->GetAttributes() & FP_DNP ) )
71 continue;
72
73 if( aExcludeBOM && ( footprint->GetAttributes() & FP_EXCLUDE_FROM_BOM ) )
74 continue;
75
76 if( footprint->GetLayer() == aLayer )
77 fp_list.push_back( footprint );
78 }
79
80 LOCALE_IO dummy_io; // Use the standard notation for float numbers
81
82 GERBER_PLOTTER plotter;
83
84 // Gerber drill file imply X2 format:
85 plotter.UseX2format( true );
86 plotter.UseX2NetAttributes( true );
87
88 // Add the standard X2 header, without FileFunction
89 AddGerberX2Header( &plotter, m_pcb );
90 plotter.SetViewport( m_offset, pcbIUScale.IU_PER_MILS/10, /* scale */ 1.0, /* mirror */false );
91
92 // has meaning only for gerber plotter. Must be called only after SetViewport
93 plotter.SetGerberCoordinatesFormat( 6 );
94 plotter.SetCreator( wxT( "PCBNEW" ) );
95
96 // Add the standard X2 FileFunction for P&P files
97 // %TF.FileFunction,Component,Ln,[top][bottom]*%
98 wxString text;
99 text.Printf( wxT( "%%TF.FileFunction,Component,L%d,%s*%%" ),
100 aLayer == B_Cu ? m_pcb->GetCopperLayerCount() : 1,
101 aLayer == B_Cu ? wxT( "Bot" ) : wxT( "Top" ) );
102 plotter.AddLineToHeader( text );
103
104 // Add file polarity (positive)
105 text = wxT( "%TF.FilePolarity,Positive*%" );
106 plotter.AddLineToHeader( text );
107
108 if( !plotter.OpenFile( aFullFilename ) )
109 return -1;
110
111 // We need a BRDITEMS_PLOTTER to plot pads
112 BRDITEMS_PLOTTER brd_plotter( &plotter, m_pcb, plotOpts );
113
114 plotter.StartPlot( wxT( "1" ) );
115
116 // Some tools in P&P files have the type and size defined.
117 // they are position flash (round), pad1 flash (diamond), other pads flash (round)
118 // and component outline thickness (polyline)
119
120 // defined size for footprint position shape (circle)
121 int flash_position_shape_diam = pcbIUScale.mmToIU( 0.3 );
122
123 // defined size for pad 1 position (diamond)
124 int pad1_mark_size = pcbIUScale.mmToIU( 0.36 );
125
126 // Normalized size for other pads (circle)
127 // It was initially the size 0, but was changed later to 0.1 mm in rev 2023-08
128 // See ComponentPin aperture attribute (see 5.6.10 .AperFunction value)
129 int other_pads_mark_size = pcbIUScale.mmToIU( 0.1 );
130
131 // defined size for component outlines
132 int line_thickness = pcbIUScale.mmToIU( 0.1 );
133
134 brd_plotter.SetLayerSet( LSET( { aLayer } ) );
135 int cmp_count = 0;
136 const bool allowUtf8 = true;
137 const bool quoteOption = false;
138
139 // Plot components data: position, outlines, pad1 and other pads.
140 for( FOOTPRINT* footprint : fp_list )
141 {
142 // Manage the aperture attribute component position:
143 GBR_METADATA metadata;
145
146 // Add object attribute: component reference to flash (mainly useful for users)
147 // using not quoted UTF8 string
148 wxString ref = ConvertNotAllowedCharsInGerber( footprint->Reference().GetShownText( false ),
149 allowUtf8, quoteOption );
150
151 metadata.SetCmpReference( ref );
153
154 // Add P&P specific attributes
155 GBR_CMP_PNP_METADATA pnpAttrib;
156
157 // Add rotation info (rotation is CCW, in degrees):
158 pnpAttrib.m_Orientation = mapRotationAngle( footprint->GetOrientationDegrees(),
159 aLayer == B_Cu ? true : false );
160
162
163 if( footprint->GetAttributes() & FP_THROUGH_HOLE )
165 else if( footprint->GetAttributes() & FP_SMD )
167
168 // Add component value info:
169 pnpAttrib.m_Value = ConvertNotAllowedCharsInGerber( footprint->Value().GetShownText( false ),
170 allowUtf8, quoteOption );
171
172 // Add component footprint info:
173 wxString fp_info = From_UTF8( footprint->GetFPID().GetLibItemName().c_str() );
174 pnpAttrib.m_Footprint = ConvertNotAllowedCharsInGerber( fp_info, allowUtf8, quoteOption );
175
176 // Add footprint lib name:
177 fp_info = From_UTF8( footprint->GetFPID().GetLibNickname().c_str() );
178 pnpAttrib.m_LibraryName = ConvertNotAllowedCharsInGerber( fp_info, allowUtf8, quoteOption );
179
181
182 VECTOR2I flash_pos = footprint->GetPosition();
183
184 plotter.FlashPadCircle( flash_pos, flash_position_shape_diam, &metadata );
186
187 // Now some extra metadata is output, avoid blindly clearing the full metadata list
189
190 // We plot the footprint courtyard when possible.
191 // If not, the pads bounding box will be used.
192 bool useFpPadsBbox = true;
193 bool onBack = aLayer == B_Cu;
194
195 footprint->BuildCourtyardCaches();
196
197 int checkFlag = onBack ? MALFORMED_B_COURTYARD : MALFORMED_F_COURTYARD;
198
199 if( ( footprint->GetFlags() & checkFlag ) == 0 )
200 {
202
203 const SHAPE_POLY_SET& courtyard = footprint->GetCourtyard( aLayer );
204
205 for( int ii = 0; ii < courtyard.OutlineCount(); ii++ )
206 {
207 SHAPE_LINE_CHAIN poly = courtyard.Outline( ii );
208
209 if( !poly.PointCount() )
210 continue;
211
212 useFpPadsBbox = false;
213 plotter.PLOTTER::PlotPoly( poly, FILL_T::NO_FILL, line_thickness, &metadata );
214 }
215 }
216
217 if( useFpPadsBbox )
218 {
220
221 // bbox of fp pads, pos 0, rot 0, non flipped
222 BOX2I bbox = footprint->GetFpPadsLocalBbox();
223
224 // negate bbox Y values if the fp is flipped (always flipped around X axis
225 // in Gerber P&P files).
226 int y_sign = aLayer == B_Cu ? -1 : 1;
227
228 SHAPE_LINE_CHAIN poly;
229 poly.Append( bbox.GetLeft(), y_sign*bbox.GetTop() );
230 poly.Append( bbox.GetLeft(), y_sign*bbox.GetBottom() );
231 poly.Append( bbox.GetRight(), y_sign*bbox.GetBottom() );
232 poly.Append( bbox.GetRight(), y_sign*bbox.GetTop() );
233 poly.SetClosed( true );
234
235 poly.Rotate( footprint->GetOrientation() );
236 poly.Move( footprint->GetPosition() );
237 plotter.PLOTTER::PlotPoly( poly, FILL_T::NO_FILL, line_thickness, &metadata );
238 }
239
240 std::vector<PAD*>pad_key_list;
241
242 if( m_plotPad1Marker )
243 {
244 findPads1( pad_key_list, footprint );
245
246 for( PAD* pad1 : pad_key_list )
247 {
249 metadata.SetPadName( pad1->GetNumber(), allowUtf8, quoteOption );
250 metadata.SetPadPinFunction( pad1->GetPinFunction(), allowUtf8, quoteOption );
252
253 // Flashes a diamond at pad position:
254 plotter.FlashRegularPolygon( pad1->GetPosition(), pad1_mark_size, 4, ANGLE_0,
255 &metadata );
256 }
257 }
258
260 {
263
264 for( PAD* pad: footprint->Pads() )
265 {
266 bool skip_pad = false;
267
268 for( PAD* pad1 : pad_key_list )
269 {
270 if( pad == pad1 ) // Already plotted
271 {
272 skip_pad = true;
273 break;
274 }
275 }
276
277 if( skip_pad )
278 continue;
279
280 // Skip also pads not on the current layer, like pads only
281 // on a tech layer
282 if( !pad->IsOnLayer( aLayer ) )
283 continue;
284
285 metadata.SetPadName( pad->GetNumber(), allowUtf8, quoteOption );
286 metadata.SetPadPinFunction( pad->GetPinFunction(), allowUtf8, quoteOption );
287
288 // Flashes a round, 0 sized round shape at pad position
289 plotter.FlashPadCircle( pad->GetPosition(), other_pads_mark_size, &metadata );
290 }
291 }
292
293 plotter.ClearAllAttributes(); // Unconditionally close all .TO attributes
294
295 cmp_count++;
296 }
297
298 // Plot board outlines, if requested
299 if( aIncludeBrdEdges )
300 {
301 brd_plotter.SetLayerSet( LSET( { Edge_Cuts } ) );
302
303 // Plot edge layer and graphic items
304 for( const BOARD_ITEM* item : m_pcb->Drawings() )
305 brd_plotter.PlotBoardGraphicItem( item );
306
307 // Draw footprint other graphic items:
308 for( FOOTPRINT* footprint : fp_list )
309 {
310 for( BOARD_ITEM* item : footprint->GraphicalItems() )
311 {
312 if( item->Type() == PCB_SHAPE_T && item->GetLayer() == Edge_Cuts )
313 brd_plotter.PlotShape( static_cast<PCB_SHAPE*>( item ) );
314 }
315 }
316 }
317
318 plotter.EndPlot();
319
320 return cmp_count;
321}
322
323
324double PLACEFILE_GERBER_WRITER::mapRotationAngle( double aAngle, bool aIsFlipped )
325{
326 // Convert a KiCad footprint orientation to gerber rotation, depending on the layer
327 // Gerber rotation is:
328 // rot angle > 0 for rot CW, seen from Top side
329 // same a Pcbnew for Top side
330 // (angle + 180) for Bottom layer i.e flipped around Y axis: X axis coordinates mirrored.
331 // because Pcbnew flip around the X axis : Y coord mirrored, that is similar to mirror
332 // around Y axis + 180 deg rotation
333 if( aIsFlipped )
334 {
335 double gbr_angle = 180.0 + aAngle;
336
337 // Normalize between -180 ... + 180 deg
338 // Not mandatory, but the angle is more easy to read
339 if( gbr_angle <= -180 )
340 gbr_angle += 360.0;
341 else if( gbr_angle > 180 )
342 gbr_angle -= 360.0;
343
344 return gbr_angle;
345 }
346
347 return aAngle;
348}
349
350
351void PLACEFILE_GERBER_WRITER::findPads1( std::vector<PAD*>& aPadList, FOOTPRINT* aFootprint ) const
352{
353 // Fint the pad "1" or pad "A1"
354 // this is possible only if only one pad is found
355 // useful to place a marker in this position
356
357 for( PAD* pad : aFootprint->Pads() )
358 {
359 if( !pad->IsOnLayer( m_layer ) )
360 continue;
361
362 if( pad->GetNumber() == wxT( "1" ) || pad->GetNumber() == wxT( "A1" ) )
363 aPadList.push_back( pad );
364 }
365}
366
367
368const wxString PLACEFILE_GERBER_WRITER::GetPlaceFileName( const wxString& aFullBaseFilename,
369 PCB_LAYER_ID aLayer ) const
370{
371 // Gerber files extension is always .gbr.
372 // Therefore, to mark pnp files, add "-pnp" to the filename, and a layer id.
373 wxFileName fn = aFullBaseFilename;
374
375 wxString post_id = wxT( "-pnp_" );
376 post_id += aLayer == B_Cu ? wxT( "bottom" ) : wxT( "top" );
377 fn.SetName( fn.GetName() + post_id );
378 fn.SetExt( FILEEXT::GerberFileExtension );
379
380 return fn.GetFullPath();
381}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:79
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
constexpr coord_type GetLeft() const
Definition box2.h:228
constexpr coord_type GetRight() const
Definition box2.h:217
constexpr coord_type GetTop() const
Definition box2.h:229
constexpr coord_type GetBottom() const
Definition box2.h:222
void SetLayerSet(const LSET &aLayerMask)
Definition pcbplot.h:87
void PlotBoardGraphicItem(const BOARD_ITEM *item)
Plot items like text and graphics but not tracks and footprints.
void PlotShape(const PCB_SHAPE *aShape)
std::deque< PAD * > & Pads()
Definition footprint.h:224
@ GBR_APERTURE_ATTRIB_PAD1_POS
Aperture used for flashed pin 1 (or A1 or AA1) position in placement files.
@ GBR_APERTURE_ATTRIB_CMP_COURTYARD
Aperture used to draw component outline courtyard in placement files.
@ GBR_APERTURE_ATTRIB_CMP_POSITION
Aperture used for flashed cmp position in placement files.
@ GBR_APERTURE_ATTRIB_PADOTHER_POS
Aperture used for flashed pads position in placement files.
@ GBR_APERTURE_ATTRIB_CMP_FOOTPRINT
Aperture used to draw component footprint bounding box in placement files.
Information which can be added in a gerber P&P file as attribute of a component.
wxString FormatCmpPnPMetadata()
One line by non empty data the orientation (.CRot) and mount type (.CMnt) are always generated.
Metadata which can be added in a gerber file as attribute in X2 format.
void SetCmpReference(const wxString &aComponentRef)
void SetPadPinFunction(const wxString &aPadPinFunction, bool aUseUTF8, bool aEscapeString)
void SetApertureAttrib(GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB aApertAttribute)
GBR_NETLIST_METADATA m_NetlistMetadata
An item to handle object attribute.
void SetNetAttribType(int aNetAttribType)
void SetPadName(const wxString &aPadname, bool aUseUTF8=false, bool aEscapeString=false)
@ GBR_NETINFO_CMP
print info associated to a component (TO.C attribute)
@ GBR_NETINFO_PAD
print info associated to a flashed pad (TO.P attribute)
void ClearExtraData()
Clear the extra data string printed at end of net attributes.
bool m_TryKeepPreviousAttributes
If true, do not clear all attributes when a attribute has changed.
void SetExtraData(const wxString &aExtraData)
Set the extra data string printed at end of net attributes.
virtual void SetGerberCoordinatesFormat(int aResolution, bool aUseInches=false) override
Selection of Gerber units and resolution (number of digits in mantissa).
void ClearAllAttributes()
Remove (clear) all attributes from object attributes dictionary (TO.
virtual void FlashPadCircle(const VECTOR2I &pos, int diametre, void *aData) override
Filled circular flashes are stored as apertures.
virtual void FlashRegularPolygon(const VECTOR2I &aShapePos, int aDiameter, int aCornerCount, const EDA_ANGLE &aOrient, void *aData) override
Flash a regular polygon.
virtual void SetViewport(const VECTOR2I &aOffset, double aIusPerDecimil, double aScale, bool aMirror) override
Set the plot offset and scaling for the current plot.
virtual bool EndPlot() override
void UseX2format(bool aEnable)
void UseX2NetAttributes(bool aEnable)
virtual bool StartPlot(const wxString &pageNumber) override
Write GERBER header to file initialize global variable g_Plot_PlotOutputFile.
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition locale_io.h:41
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
Definition pad.h:54
Parameters and options when plotting/printing a board.
bool GetUseAuxOrigin() const
const wxString GetPlaceFileName(const wxString &aFullBaseFilename, PCB_LAYER_ID aLayer) const
double mapRotationAngle(double aAngle, bool aIsFlipped)
Convert a KiCad footprint orientation to gerber rotation both are in degrees.
int CreatePlaceFile(const wxString &aFullFilename, PCB_LAYER_ID aLayer, bool aIncludeBrdEdges, bool aExcludeDNP, bool aExcludeBOM)
Create an pnp gerber file.
void findPads1(std::vector< PAD * > &aPadList, FOOTPRINT *aFootprint) const
Find the pad(s) 1 (or pad "A1") of a footprint.
virtual bool OpenFile(const wxString &aFullFilename)
Open or create the plot file aFullFilename.
Definition plotter.cpp:76
virtual void SetCreator(const wxString &aCreator)
Definition plotter.h:173
void AddLineToHeader(const wxString &aExtraString)
Add a line to the list of free lines to print at the beginning of the file.
Definition plotter.h:183
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void Move(const VECTOR2I &aVector) override
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int OutlineCount() const
Return the number of outlines in the set.
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
#define MALFORMED_F_COURTYARD
#define MALFORMED_B_COURTYARD
@ NO_FILL
Definition eda_shape.h:57
@ FP_SMD
Definition footprint.h:82
@ FP_DNP
Definition footprint.h:87
@ FP_EXCLUDE_FROM_POS_FILES
Definition footprint.h:83
@ FP_EXCLUDE_FROM_BOM
Definition footprint.h:84
@ FP_THROUGH_HOLE
Definition footprint.h:81
wxString ConvertNotAllowedCharsInGerber(const wxString &aString, bool aAllowUtf8Chars, bool aQuoteString)
Normalize aString and convert it to a Gerber compatible wxString.
Handle special data (items attributes) during plot.
Classes used in place file generation.
static const std::string GerberFileExtension
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ Edge_Cuts
Definition layer_ids.h:112
@ B_Cu
Definition layer_ids.h:65
@ UNDEFINED_LAYER
Definition layer_ids.h:61
This file contains miscellaneous commonly used macros and functions.
void AddGerberX2Header(PLOTTER *aPlotter, const BOARD *aBoard, bool aUseX1CompatibilityMode)
Calculate some X2 attributes as defined in the Gerber file format specification J4 (chapter 5) and ad...
Definition pcbplot.cpp:294
wxString From_UTF8(const char *cstring)
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:88
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
Definition of file extensions used in Kicad.