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->GetExcludedFromPosFilesForVariant( m_variant ) )
68 continue;
69
70 if( aExcludeDNP && footprint->GetDNPForVariant( m_variant ) )
71 continue;
72
73 if( aExcludeBOM && footprint->GetExcludedFromBOMForVariant( m_variant ) )
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 wxString fpValue = UnescapeString(
170 footprint->GetFieldValueForVariant( m_variant,
172 pnpAttrib.m_Value = ConvertNotAllowedCharsInGerber( fpValue, allowUtf8, quoteOption );
173
174 // Add component footprint info:
175 wxString fp_info = From_UTF8( footprint->GetFPID().GetLibItemName().c_str() );
176 pnpAttrib.m_Footprint = ConvertNotAllowedCharsInGerber( fp_info, allowUtf8, quoteOption );
177
178 // Add footprint lib name:
179 fp_info = From_UTF8( footprint->GetFPID().GetLibNickname().c_str() );
180 pnpAttrib.m_LibraryName = ConvertNotAllowedCharsInGerber( fp_info, allowUtf8, quoteOption );
181
183
184 VECTOR2I flash_pos = footprint->GetPosition();
185
186 plotter.FlashPadCircle( flash_pos, flash_position_shape_diam, &metadata );
188
189 // Now some extra metadata is output, avoid blindly clearing the full metadata list
191
192 // We plot the footprint courtyard when possible.
193 // If not, the pads bounding box will be used.
194 bool useFpPadsBbox = true;
195 bool onBack = aLayer == B_Cu;
196
197 footprint->BuildCourtyardCaches();
198
199 int checkFlag = onBack ? MALFORMED_B_COURTYARD : MALFORMED_F_COURTYARD;
200
201 if( ( footprint->GetFlags() & checkFlag ) == 0 )
202 {
204
205 const SHAPE_POLY_SET& courtyard = footprint->GetCourtyard( aLayer );
206
207 for( int ii = 0; ii < courtyard.OutlineCount(); ii++ )
208 {
209 SHAPE_LINE_CHAIN poly = courtyard.Outline( ii );
210
211 if( !poly.PointCount() )
212 continue;
213
214 useFpPadsBbox = false;
215 plotter.PLOTTER::PlotPoly( poly, FILL_T::NO_FILL, line_thickness, &metadata );
216 }
217 }
218
219 if( useFpPadsBbox )
220 {
222
223 // bbox of fp pads, pos 0, rot 0, non flipped
224 BOX2I bbox = footprint->GetFpPadsLocalBbox();
225
226 // negate bbox Y values if the fp is flipped (always flipped around X axis
227 // in Gerber P&P files).
228 int y_sign = aLayer == B_Cu ? -1 : 1;
229
230 SHAPE_LINE_CHAIN poly;
231 poly.Append( bbox.GetLeft(), y_sign*bbox.GetTop() );
232 poly.Append( bbox.GetLeft(), y_sign*bbox.GetBottom() );
233 poly.Append( bbox.GetRight(), y_sign*bbox.GetBottom() );
234 poly.Append( bbox.GetRight(), y_sign*bbox.GetTop() );
235 poly.SetClosed( true );
236
237 poly.Rotate( footprint->GetOrientation() );
238 poly.Move( footprint->GetPosition() );
239 plotter.PLOTTER::PlotPoly( poly, FILL_T::NO_FILL, line_thickness, &metadata );
240 }
241
242 std::vector<PAD*>pad_key_list;
243
244 if( m_plotPad1Marker )
245 {
246 findPads1( pad_key_list, footprint );
247
248 for( PAD* pad1 : pad_key_list )
249 {
251 metadata.SetPadName( pad1->GetNumber(), allowUtf8, quoteOption );
252 metadata.SetPadPinFunction( pad1->GetPinFunction(), allowUtf8, quoteOption );
254
255 // Flashes a diamond at pad position:
256 plotter.FlashRegularPolygon( pad1->GetPosition(), pad1_mark_size, 4, ANGLE_0,
257 &metadata );
258 }
259 }
260
262 {
265
266 for( PAD* pad: footprint->Pads() )
267 {
268 bool skip_pad = false;
269
270 for( PAD* pad1 : pad_key_list )
271 {
272 if( pad == pad1 ) // Already plotted
273 {
274 skip_pad = true;
275 break;
276 }
277 }
278
279 if( skip_pad )
280 continue;
281
282 // Skip also pads not on the current layer, like pads only
283 // on a tech layer
284 if( !pad->IsOnLayer( aLayer ) )
285 continue;
286
287 metadata.SetPadName( pad->GetNumber(), allowUtf8, quoteOption );
288 metadata.SetPadPinFunction( pad->GetPinFunction(), allowUtf8, quoteOption );
289
290 // Flashes a round, 0 sized round shape at pad position
291 plotter.FlashPadCircle( pad->GetPosition(), other_pads_mark_size, &metadata );
292 }
293 }
294
295 plotter.ClearAllAttributes(); // Unconditionally close all .TO attributes
296
297 cmp_count++;
298 }
299
300 // Plot board outlines, if requested
301 if( aIncludeBrdEdges )
302 {
303 brd_plotter.SetLayerSet( LSET( { Edge_Cuts } ) );
304
305 // Plot edge layer and graphic items
306 for( const BOARD_ITEM* item : m_pcb->Drawings() )
307 brd_plotter.PlotBoardGraphicItem( item );
308
309 // Draw footprint other graphic items:
310 for( FOOTPRINT* footprint : fp_list )
311 {
312 for( BOARD_ITEM* item : footprint->GraphicalItems() )
313 {
314 if( item->Type() == PCB_SHAPE_T && item->GetLayer() == Edge_Cuts )
315 brd_plotter.PlotShape( static_cast<PCB_SHAPE*>( item ) );
316 }
317 }
318 }
319
320 plotter.EndPlot();
321
322 return cmp_count;
323}
324
325
326double PLACEFILE_GERBER_WRITER::mapRotationAngle( double aAngle, bool aIsFlipped )
327{
328 // Convert a KiCad footprint orientation to gerber rotation, depending on the layer
329 // Gerber rotation is:
330 // rot angle > 0 for rot CW, seen from Top side
331 // same a Pcbnew for Top side
332 // (angle + 180) for Bottom layer i.e flipped around Y axis: X axis coordinates mirrored.
333 // because Pcbnew flip around the X axis : Y coord mirrored, that is similar to mirror
334 // around Y axis + 180 deg rotation
335 if( aIsFlipped )
336 {
337 double gbr_angle = 180.0 + aAngle;
338
339 // Normalize between -180 ... + 180 deg
340 // Not mandatory, but the angle is more easy to read
341 if( gbr_angle <= -180 )
342 gbr_angle += 360.0;
343 else if( gbr_angle > 180 )
344 gbr_angle -= 360.0;
345
346 return gbr_angle;
347 }
348
349 return aAngle;
350}
351
352
353void PLACEFILE_GERBER_WRITER::findPads1( std::vector<PAD*>& aPadList, FOOTPRINT* aFootprint ) const
354{
355 // Fint the pad "1" or pad "A1"
356 // this is possible only if only one pad is found
357 // useful to place a marker in this position
358
359 for( PAD* pad : aFootprint->Pads() )
360 {
361 if( !pad->IsOnLayer( m_layer ) )
362 continue;
363
364 if( pad->GetNumber() == wxT( "1" ) || pad->GetNumber() == wxT( "A1" ) )
365 aPadList.push_back( pad );
366 }
367}
368
369
370const wxString PLACEFILE_GERBER_WRITER::GetPlaceFileName( const wxString& aFullBaseFilename,
371 PCB_LAYER_ID aLayer ) const
372{
373 // Gerber files extension is always .gbr.
374 // Therefore, to mark pnp files, add "-pnp" to the filename, and a layer id.
375 wxFileName fn = aFullBaseFilename;
376
377 wxString post_id = wxT( "-pnp_" );
378 post_id += aLayer == B_Cu ? wxT( "bottom" ) : wxT( "top" );
379 fn.SetName( fn.GetName() + post_id );
380 fn.SetExt( FILEEXT::GerberFileExtension );
381
382 return fn.GetFullPath();
383}
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:84
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
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:326
@ 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:55
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:77
virtual void SetCreator(const wxString &aCreator)
Definition plotter.h:188
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:198
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:59
@ FP_SMD
Definition footprint.h:85
@ FP_THROUGH_HOLE
Definition footprint.h:84
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 UnescapeString(const wxString &aSource)
wxString From_UTF8(const char *cstring)
@ VALUE
Field Value of part, i.e. "3.3K".
wxString GetCanonicalFieldName(FIELD_T aFieldType)
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:85
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687
Definition of file extensions used in Kicad.