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 (C) 1992-2023 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
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 )
53{
54 m_layer = aLayer;
55
57
58 if( plotOpts.GetUseAuxOrigin() )
60
61 // Collect footprints on the right layer
62 std::vector<FOOTPRINT*> fp_list;
63
64 for( FOOTPRINT* footprint : m_pcb->Footprints() )
65 {
66 if( footprint->GetAttributes() & FP_EXCLUDE_FROM_POS_FILES )
67 continue;
68
69 if( footprint->GetLayer() == aLayer )
70 fp_list.push_back( footprint );
71 }
72
73 LOCALE_IO dummy_io; // Use the standard notation for float numbers
74
75 GERBER_PLOTTER plotter;
76
77 // Gerber drill file imply X2 format:
78 plotter.UseX2format( true );
79 plotter.UseX2NetAttributes( true );
80
81 // Add the standard X2 header, without FileFunction
82 AddGerberX2Header( &plotter, m_pcb );
83 plotter.SetViewport( m_offset, pcbIUScale.IU_PER_MILS/10, /* scale */ 1.0, /* mirror */false );
84
85 // has meaning only for gerber plotter. Must be called only after SetViewport
86 plotter.SetGerberCoordinatesFormat( 6 );
87 plotter.SetCreator( wxT( "PCBNEW" ) );
88
89 // Add the standard X2 FileFunction for P&P files
90 // %TF.FileFunction,Component,Ln,[top][bottom]*%
91 wxString text;
92 text.Printf( wxT( "%%TF.FileFunction,Component,L%d,%s*%%" ),
93 aLayer == B_Cu ? m_pcb->GetCopperLayerCount() : 1,
94 aLayer == B_Cu ? wxT( "Bot" ) : wxT( "Top" ) );
95 plotter.AddLineToHeader( text );
96
97 // Add file polarity (positive)
98 text = wxT( "%TF.FilePolarity,Positive*%" );
99 plotter.AddLineToHeader( text );
100
101 if( !plotter.OpenFile( aFullFilename ) )
102 return -1;
103
104 // We need a BRDITEMS_PLOTTER to plot pads
105 BRDITEMS_PLOTTER brd_plotter( &plotter, m_pcb, plotOpts );
106
107 plotter.StartPlot( wxT( "1" ) );
108
109 // Some tools in P&P files have the type and size defined.
110 // they are position flash (round), pad1 flash (diamond), other pads flash (round)
111 // and component outline thickness (polyline)
112
113 // defined size for footprint position shape (circle)
114 int flash_position_shape_diam = pcbIUScale.mmToIU( 0.3 );
115
116 // defined size for pad 1 position (diamond)
117 int pad1_mark_size = pcbIUScale.mmToIU( 0.36 );
118
119 // Normalized size for other pads (circle)
120 // It was initially the size 0, but was changed later to 0.1 mm in rev 2023-08
121 // See ComponentPin aperture attribute (see 5.6.10 .AperFunction value)
122 int other_pads_mark_size = pcbIUScale.mmToIU( 0.1 );
123
124 // defined size for component outlines
125 int line_thickness = pcbIUScale.mmToIU( 0.1 );
126
127 brd_plotter.SetLayerSet( LSET( { aLayer } ) );
128 int cmp_count = 0;
129 const bool allowUtf8 = true;
130 const bool quoteOption = false;
131
132 // Plot components data: position, outlines, pad1 and other pads.
133 for( FOOTPRINT* footprint : fp_list )
134 {
135 // Manage the aperture attribute component position:
136 GBR_METADATA metadata;
138
139 // Add object attribute: component reference to flash (mainly useful for users)
140 // using not quoted UTF8 string
141 wxString ref = ConvertNotAllowedCharsInGerber( footprint->Reference().GetShownText( false ),
142 allowUtf8, quoteOption );
143
144 metadata.SetCmpReference( ref );
146
147 // Add P&P specific attributes
148 GBR_CMP_PNP_METADATA pnpAttrib;
149
150 // Add rotation info (rotation is CCW, in degrees):
151 pnpAttrib.m_Orientation = mapRotationAngle( footprint->GetOrientationDegrees(),
152 aLayer == B_Cu ? true : false );
153
155
156 if( footprint->GetAttributes() & FP_THROUGH_HOLE )
158 else if( footprint->GetAttributes() & FP_SMD )
160
161 // Add component value info:
162 pnpAttrib.m_Value = ConvertNotAllowedCharsInGerber( footprint->Value().GetShownText( false ),
163 allowUtf8, quoteOption );
164
165 // Add component footprint info:
166 wxString fp_info = From_UTF8( footprint->GetFPID().GetLibItemName().c_str() );
167 pnpAttrib.m_Footprint = ConvertNotAllowedCharsInGerber( fp_info, allowUtf8, quoteOption );
168
169 // Add footprint lib name:
170 fp_info = From_UTF8( footprint->GetFPID().GetLibNickname().c_str() );
171 pnpAttrib.m_LibraryName = ConvertNotAllowedCharsInGerber( fp_info, allowUtf8, quoteOption );
172
174
175 VECTOR2I flash_pos = footprint->GetPosition();
176
177 plotter.FlashPadCircle( flash_pos, flash_position_shape_diam, FILLED, &metadata );
179
180 // Now some extra metadata is output, avoid blindly clearing the full metadata list
182
183 // We plot the footprint courtyard when possible.
184 // If not, the pads bounding box will be used.
185 bool useFpPadsBbox = true;
186 bool onBack = aLayer == B_Cu;
187
188 footprint->BuildCourtyardCaches();
189
190 int checkFlag = onBack ? MALFORMED_B_COURTYARD : MALFORMED_F_COURTYARD;
191
192 if( ( footprint->GetFlags() & checkFlag ) == 0 )
193 {
195
196 const SHAPE_POLY_SET& courtyard = footprint->GetCourtyard( aLayer );
197
198 for( int ii = 0; ii < courtyard.OutlineCount(); ii++ )
199 {
200 SHAPE_LINE_CHAIN poly = courtyard.Outline( ii );
201
202 if( !poly.PointCount() )
203 continue;
204
205 useFpPadsBbox = false;
206 plotter.PLOTTER::PlotPoly( poly, FILL_T::NO_FILL, line_thickness, &metadata );
207 }
208 }
209
210 if( useFpPadsBbox )
211 {
213
214 // bbox of fp pads, pos 0, rot 0, non flipped
215 BOX2I bbox = footprint->GetFpPadsLocalBbox();
216
217 // negate bbox Y values if the fp is flipped (always flipped around X axis
218 // in Gerber P&P files).
219 int y_sign = aLayer == B_Cu ? -1 : 1;
220
221 SHAPE_LINE_CHAIN poly;
222 poly.Append( bbox.GetLeft(), y_sign*bbox.GetTop() );
223 poly.Append( bbox.GetLeft(), y_sign*bbox.GetBottom() );
224 poly.Append( bbox.GetRight(), y_sign*bbox.GetBottom() );
225 poly.Append( bbox.GetRight(), y_sign*bbox.GetTop() );
226 poly.SetClosed( true );
227
228 poly.Rotate( footprint->GetOrientation() );
229 poly.Move( footprint->GetPosition() );
230 plotter.PLOTTER::PlotPoly( poly, FILL_T::NO_FILL, line_thickness, &metadata );
231 }
232
233 std::vector<PAD*>pad_key_list;
234
235 if( m_plotPad1Marker )
236 {
237 findPads1( pad_key_list, footprint );
238
239 for( PAD* pad1 : pad_key_list )
240 {
242 metadata.SetPadName( pad1->GetNumber(), allowUtf8, quoteOption );
243 metadata.SetPadPinFunction( pad1->GetPinFunction(), allowUtf8, quoteOption );
245
246 // Flashes a diamond at pad position:
247 plotter.FlashRegularPolygon( pad1->GetPosition(), pad1_mark_size, 4, ANGLE_0,
248 FILLED, &metadata );
249 }
250 }
251
253 {
256
257 for( PAD* pad: footprint->Pads() )
258 {
259 bool skip_pad = false;
260
261 for( PAD* pad1 : pad_key_list )
262 {
263 if( pad == pad1 ) // Already plotted
264 {
265 skip_pad = true;
266 break;
267 }
268 }
269
270 if( skip_pad )
271 continue;
272
273 // Skip also pads not on the current layer, like pads only
274 // on a tech layer
275 if( !pad->IsOnLayer( aLayer ) )
276 continue;
277
278 metadata.SetPadName( pad->GetNumber(), allowUtf8, quoteOption );
279 metadata.SetPadPinFunction( pad->GetPinFunction(), allowUtf8, quoteOption );
280
281 // Flashes a round, 0 sized round shape at pad position
282 plotter.FlashPadCircle( pad->GetPosition(), other_pads_mark_size, FILLED, &metadata );
283 }
284 }
285
286 plotter.ClearAllAttributes(); // Unconditionally close all .TO attributes
287
288 cmp_count++;
289 }
290
291 // Plot board outlines, if requested
292 if( aIncludeBrdEdges )
293 {
294 brd_plotter.SetLayerSet( LSET( { Edge_Cuts } ) );
295
296 // Plot edge layer and graphic items
297 for( const BOARD_ITEM* item : m_pcb->Drawings() )
298 brd_plotter.PlotBoardGraphicItem( item );
299
300 // Draw footprint other graphic items:
301 for( FOOTPRINT* footprint : fp_list )
302 {
303 for( BOARD_ITEM* item : footprint->GraphicalItems() )
304 {
305 if( item->Type() == PCB_SHAPE_T && item->GetLayer() == Edge_Cuts )
306 brd_plotter.PlotShape( static_cast<PCB_SHAPE*>( item ) );
307 }
308 }
309 }
310
311 plotter.EndPlot();
312
313 return cmp_count;
314}
315
316
317double PLACEFILE_GERBER_WRITER::mapRotationAngle( double aAngle, bool aIsFlipped )
318{
319 // Convert a KiCad footprint orientation to gerber rotation, depending on the layer
320 // Gerber rotation is:
321 // rot angle > 0 for rot CW, seen from Top side
322 // same a Pcbnew for Top side
323 // (angle + 180) for Bottom layer i.e flipped around Y axis: X axis coordinates mirrored.
324 // because Pcbnew flip around the X axis : Y coord mirrored, that is similar to mirror
325 // around Y axis + 180 deg rotation
326 if( aIsFlipped )
327 {
328 double gbr_angle = 180.0 + aAngle;
329
330 // Normalize between -180 ... + 180 deg
331 // Not mandatory, but the angle is more easy to read
332 if( gbr_angle <= -180 )
333 gbr_angle += 360.0;
334 else if( gbr_angle > 180 )
335 gbr_angle -= 360.0;
336
337 return gbr_angle;
338 }
339
340 return aAngle;
341}
342
343
344void PLACEFILE_GERBER_WRITER::findPads1( std::vector<PAD*>& aPadList, FOOTPRINT* aFootprint ) const
345{
346 // Fint the pad "1" or pad "A1"
347 // this is possible only if only one pad is found
348 // useful to place a marker in this position
349
350 for( PAD* pad : aFootprint->Pads() )
351 {
352 if( !pad->IsOnLayer( m_layer ) )
353 continue;
354
355 if( pad->GetNumber() == wxT( "1" ) || pad->GetNumber() == wxT( "A1" ) )
356 aPadList.push_back( pad );
357 }
358}
359
360
361const wxString PLACEFILE_GERBER_WRITER::GetPlaceFileName( const wxString& aFullBaseFilename,
362 PCB_LAYER_ID aLayer ) const
363{
364 // Gerber files extension is always .gbr.
365 // Therefore, to mark pnp files, add "-pnp" to the filename, and a layer id.
366 wxFileName fn = aFullBaseFilename;
367
368 wxString post_id = wxT( "-pnp_" );
369 post_id += aLayer == B_Cu ? wxT( "bottom" ) : wxT( "top" );
370 fn.SetName( fn.GetName() + post_id );
371 fn.SetExt( FILEEXT::GerberFileExtension );
372
373 return fn.GetFullPath();
374}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
const VECTOR2I & GetAuxOrigin()
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:290
int GetCopperLayerCount() const
Definition: board.cpp:741
const FOOTPRINTS & Footprints() const
Definition: board.h:331
const PCB_PLOT_PARAMS & GetPlotOptions() const
Definition: board.h:692
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:895
const DRAWINGS & Drawings() const
Definition: board.h:333
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 PlotBoardGraphicItem(const BOARD_ITEM *item)
Plot items like text and graphics but not tracks and footprints.
void SetLayerSet(LSET aLayerMask)
Definition: pcbplot.h:86
void PlotShape(const PCB_SHAPE *aShape)
std::deque< PAD * > & Pads()
Definition: footprint.h:206
@ GBR_APERTURE_ATTRIB_PAD1_POS
aperture used for flashed pads position in placement files.
Definition: gbr_metadata.h:149
@ GBR_APERTURE_ATTRIB_CMP_POSITION
aperture used for flashed pin 1 (or A1 or AA1) position in placement files.
Definition: gbr_metadata.h:146
@ GBR_APERTURE_ATTRIB_PADOTHER_POS
aperture used to draw component physical body outline without pins in placement files.
Definition: gbr_metadata.h:152
@ GBR_APERTURE_ATTRIB_CMP_FOOTPRINT
aperture used to draw component outline courtyard in placement files.
Definition: gbr_metadata.h:161
Information which can be added in a gerber P&P file as attribute of a component.
wxString FormatCmpPnPMetadata()
Metadata which can be added in a gerber file as attribute in X2 format.
Definition: gbr_metadata.h:205
void SetCmpReference(const wxString &aComponentRef)
Definition: gbr_metadata.h:241
void SetPadPinFunction(const wxString &aPadPinFunction, bool aUseUTF8, bool aEscapeString)
Definition: gbr_metadata.h:236
void SetApertureAttrib(GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB aApertAttribute)
Definition: gbr_metadata.h:209
GBR_NETLIST_METADATA m_NetlistMetadata
An item to handle object attribute.
Definition: gbr_metadata.h:262
void SetNetAttribType(int aNetAttribType)
Definition: gbr_metadata.h:219
void SetPadName(const wxString &aPadname, bool aUseUTF8=false, bool aEscapeString=false)
Definition: gbr_metadata.h:231
@ 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, OUTLINE_MODE trace_mode, void *aData) override
Filled circular flashes are stored as apertures.
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 void FlashRegularPolygon(const VECTOR2I &aShapePos, int aDiameter, int aCornerCount, const EDA_ANGLE &aOrient, OUTLINE_MODE aTraceMode, void *aData) override
Flash a regular polygon.
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:49
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:36
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)
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:75
virtual void SetCreator(const wxString &aCreator)
Definition: plotter.h:154
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:164
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:401
#define MALFORMED_F_COURTYARD
#define MALFORMED_B_COURTYARD
@ FP_SMD
Definition: footprint.h:76
@ FP_EXCLUDE_FROM_POS_FILES
Definition: footprint.h:77
@ FP_THROUGH_HOLE
Definition: footprint.h:75
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 bool FILLED
Definition: gr_basic.cpp:30
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
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)
const double IU_PER_MILS
Definition: base_units.h:77
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
Definition of file extensions used in Kicad.