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) 2021 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
21 /**
22 * @file GERBER_plotter.cpp
23 * @brief specialized plotter for GERBER files format
24 */
25
26 #include <string_utils.h>
27 #include <convert_basic_shapes_to_polygon.h>
28 #include <macros.h>
29 #include <math/util.h> // for KiROUND
30 #include <trigo.h>
31 #include <wx/log.h>
32
33 #include <build_version.h>
34
35 #include <plotters/plotter_gerber.h>
36 #include <plotters/gbr_plotter_aperture_macros.h>
37
38 #include <gbr_metadata.h>
39
40
41 // if GBR_USE_MACROS is defined, pads having a shape that is not a Gerber primitive
42 // will use a macro when possible
43 // Old code will be removed only after many tests
44 //
45 // Note also: setting m_gerberDisableApertMacros to true disable all aperture macros
46 // in Gerber files
47 //
48 #define GBR_USE_MACROS_FOR_CHAMFERED_ROUND_RECT
49 #define GBR_USE_MACROS_FOR_CHAMFERED_RECT
50 #define GBR_USE_MACROS_FOR_ROUNDRECT
51 #define GBR_USE_MACROS_FOR_TRAPEZOID
52 #define GBR_USE_MACROS_FOR_ROTATED_OVAL
53 #define GBR_USE_MACROS_FOR_ROTATED_RECT
54 #define GBR_USE_MACROS_FOR_CUSTOM_PAD
55
56 // max count of corners to create a aperture macro for a custom shape.
57 // provided just in case a aperture macro type free polygon creates issues
58 // when the number of corners is too high.
59 // (1 corner = up to 24 chars)
60 // Gerber doc say max corners 5000. We use a slightly smaller value.
61 // if a custom shape needs more than GBR_MACRO_FOR_CUSTOM_PAD_MAX_CORNER_COUNT, it
62 // will be plot using a region.
63 #define GBR_MACRO_FOR_CUSTOM_PAD_MAX_CORNER_COUNT 4990
64 #define AM_FREEPOLY_BASENAME "FreePoly"
65
66
67 // A helper function to compare 2 polygons: polygons are similar if they have the same
68 // number of vertices and each vertex coordinate are similar, i.e. if the difference
69 // between coordinates is small ( <= margin to accept rounding issues coming from polygon
70 // geometric transforms like rotation
polyCompare(const std::vector<wxPoint> & aPolygon,const std::vector<wxPoint> & aTestPolygon)71 static bool polyCompare( const std::vector<wxPoint>& aPolygon,
72 const std::vector<wxPoint>& aTestPolygon )
73 {
74 // fast test: polygon sizes must be the same:
75 if( aTestPolygon.size() != aPolygon.size() )
76 return false;
77
78 const int margin = 2;
79
80 for( size_t jj = 0; jj < aPolygon.size(); jj++ )
81 {
82 if( std::abs( aPolygon[jj].x - aTestPolygon[jj].x ) > margin ||
83 std::abs( aPolygon[jj].y - aTestPolygon[jj].y ) > margin )
84 return false;
85 }
86
87 return true;
88 }
89
90
GERBER_PLOTTER()91 GERBER_PLOTTER::GERBER_PLOTTER()
92 {
93 workFile = nullptr;
94 finalFile = nullptr;
95 m_currentApertureIdx = -1;
96 m_apertureAttribute = 0;
97
98 // number of digits after the point (number of digits of the mantissa
99 // Be careful: the Gerber coordinates are stored in an integer
100 // so 6 digits (inches) or 5 digits (mm) is a good value
101 // To avoid overflow, 7 digits (inches) or 6 digits is a max.
102 // with lower values than 6 digits (inches) or 5 digits (mm),
103 // Creating self-intersecting polygons from non-intersecting polygons
104 // happen easily.
105 m_gerberUnitInch = false;
106 m_gerberUnitFmt = 6;
107 m_useX2format = true;
108 m_useNetAttributes = true;
109 m_gerberDisableApertMacros = false;
110
111 m_hasApertureRoundRect = false; // true is at least one round rect aperture is in use
112 m_hasApertureRotOval = false; // true is at least one oval rotated aperture is in use
113 m_hasApertureRotRect = false; // true is at least one rect. rotated aperture is in use
114 m_hasApertureOutline4P = false; // true is at least one rotated rect or trapezoid pad
115 // aperture is in use
116 m_hasApertureChamferedRect = false; // true is at least one chamfered rect
117 // (no rounded corner) is in use
118 }
119
120
SetViewport(const wxPoint & aOffset,double aIusPerDecimil,double aScale,bool aMirror)121 void GERBER_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil,
122 double aScale, bool aMirror )
123 {
124 wxASSERT( aMirror == false );
125 m_plotMirror = false;
126 m_plotOffset = aOffset;
127 wxASSERT( aScale == 1 ); // aScale parameter is not used in Gerber
128 m_plotScale = 1; // Plot scale is *always* 1.0
129
130 m_IUsPerDecimil = aIusPerDecimil;
131
132 // gives now a default value to iuPerDeviceUnit (because the units of the caller is now known)
133 // which could be modified later by calling SetGerberCoordinatesFormat()
134 m_iuPerDeviceUnit = pow( 10.0, m_gerberUnitFmt ) / ( m_IUsPerDecimil * 10000.0 );
135
136 // We don't handle the filmbox, and it's more useful to keep the
137 // origin at the origin
138 m_paperSize.x = 0;
139 m_paperSize.y = 0;
140 }
141
142
SetGerberCoordinatesFormat(int aResolution,bool aUseInches)143 void GERBER_PLOTTER::SetGerberCoordinatesFormat( int aResolution, bool aUseInches )
144 {
145 m_gerberUnitInch = aUseInches;
146 m_gerberUnitFmt = aResolution;
147
148 m_iuPerDeviceUnit = pow( 10.0, m_gerberUnitFmt ) / ( m_IUsPerDecimil * 10000.0 );
149
150 if( ! m_gerberUnitInch )
151 m_iuPerDeviceUnit *= 25.4; // gerber output in mm
152 }
153
154
emitDcode(const DPOINT & pt,int dcode)155 void GERBER_PLOTTER::emitDcode( const DPOINT& pt, int dcode )
156 {
157
158 fprintf( m_outputFile, "X%dY%dD%02d*\n", KiROUND( pt.x ), KiROUND( pt.y ), dcode );
159 }
160
ClearAllAttributes()161 void GERBER_PLOTTER::ClearAllAttributes()
162 {
163 // Remove all attributes from object attributes dictionary (TO. and TA commands)
164 if( m_useX2format )
165 fputs( "%TD*%\n", m_outputFile );
166 else
167 fputs( "G04 #@! TD*\n", m_outputFile );
168
169 m_objectAttributesDictionary.clear();
170 }
171
172
clearNetAttribute()173 void GERBER_PLOTTER::clearNetAttribute()
174 {
175 // disable a Gerber net attribute (exists only in X2 with net attributes mode).
176 if( m_objectAttributesDictionary.empty() ) // No net attribute or not X2 mode
177 return;
178
179 // Remove all net attributes from object attributes dictionary
180 if( m_useX2format )
181 fputs( "%TD*%\n", m_outputFile );
182 else
183 fputs( "G04 #@! TD*\n", m_outputFile );
184
185 m_objectAttributesDictionary.clear();
186 }
187
188
StartBlock(void * aData)189 void GERBER_PLOTTER::StartBlock( void* aData )
190 {
191 // Currently, it is the same as EndBlock(): clear all aperture net attributes
192 EndBlock( aData );
193 }
194
195
EndBlock(void * aData)196 void GERBER_PLOTTER::EndBlock( void* aData )
197 {
198 // Remove all net attributes from object attributes dictionary
199 clearNetAttribute();
200 }
201
202
formatNetAttribute(GBR_NETLIST_METADATA * aData)203 void GERBER_PLOTTER::formatNetAttribute( GBR_NETLIST_METADATA* aData )
204 {
205 // print a Gerber net attribute record.
206 // it is added to the object attributes dictionary
207 // On file, only modified or new attributes are printed.
208 if( aData == nullptr )
209 return;
210
211 if( !m_useNetAttributes )
212 return;
213
214 bool useX1StructuredComment = !m_useX2format;
215
216 bool clearDict;
217 std::string short_attribute_string;
218
219 if( !FormatNetAttribute( short_attribute_string, m_objectAttributesDictionary,
220 aData, clearDict, useX1StructuredComment ) )
221 return;
222
223 if( clearDict )
224 clearNetAttribute();
225
226 if( !short_attribute_string.empty() )
227 fputs( short_attribute_string.c_str(), m_outputFile );
228
229 if( m_useX2format && !aData->m_ExtraData.IsEmpty() )
230 {
231 std::string extra_data = TO_UTF8( aData->m_ExtraData );
232 fputs( extra_data.c_str(), m_outputFile );
233 }
234 }
235
236
StartPlot()237 bool GERBER_PLOTTER::StartPlot()
238 {
239 m_hasApertureRoundRect = false; // true is at least one round rect aperture is in use
240 m_hasApertureRotOval = false; // true is at least one oval rotated aperture is in use
241 m_hasApertureRotRect = false; // true is at least one rect. rotated aperture is in use
242 m_hasApertureOutline4P = false; // true is at least one rotated rect/trapezoid aperture
243 // is in use
244 m_hasApertureChamferedRect = false; // true is at least one chamfered rect is in use
245 m_am_freepoly_list.ClearList();
246
247 wxASSERT( m_outputFile );
248
249 finalFile = m_outputFile; // the actual gerber file will be created later
250
251 // Create a temp file in system temp to avoid potential network share buffer issues for
252 // the final read and save.
253 m_workFilename = wxFileName::CreateTempFileName( "" );
254 workFile = wxFopen( m_workFilename, wxT( "wt" ));
255 m_outputFile = workFile;
256 wxASSERT( m_outputFile );
257
258 if( m_outputFile == nullptr )
259 return false;
260
261 for( unsigned ii = 0; ii < m_headerExtraLines.GetCount(); ii++ )
262 {
263 if( ! m_headerExtraLines[ii].IsEmpty() )
264 fprintf( m_outputFile, "%s\n", TO_UTF8( m_headerExtraLines[ii] ) );
265 }
266
267 // Set coordinate format to 3.6 or 4.5 absolute, leading zero omitted
268 // the number of digits for the integer part of coordinates is needed
269 // in gerber format, but is not very important when omitting leading zeros
270 // It is fixed here to 3 (inch) or 4 (mm), but is not actually used
271 int leadingDigitCount = m_gerberUnitInch ? 3 : 4;
272
273 fprintf( m_outputFile, "%%FSLAX%d%dY%d%d*%%\n",
274 leadingDigitCount, m_gerberUnitFmt,
275 leadingDigitCount, m_gerberUnitFmt );
276 fprintf( m_outputFile,
277 "G04 Gerber Fmt %d.%d, Leading zero omitted, Abs format (unit %s)*\n",
278 leadingDigitCount, m_gerberUnitFmt,
279 m_gerberUnitInch ? "inch" : "mm" );
280
281 wxString Title = m_creator + wxT( " " ) + GetBuildVersion();
282
283 // In gerber files, ASCII7 chars only are allowed.
284 // So use a ISO date format (using a space as separator between date and time),
285 // not a localized date format
286 wxDateTime date = wxDateTime::Now();
287 fprintf( m_outputFile, "G04 Created by KiCad (%s) date %s*\n",
288 TO_UTF8( Title ), TO_UTF8( date.FormatISOCombined( ' ') ) );
289
290 /* Mass parameter: unit = INCHES/MM */
291 if( m_gerberUnitInch )
292 fputs( "%MOIN*%\n", m_outputFile );
293 else
294 fputs( "%MOMM*%\n", m_outputFile );
295
296 // Be sure the usual dark polarity is selected:
297 fputs( "%LPD*%\n", m_outputFile );
298
299 // Set initial interpolation mode: always G01 (linear):
300 fputs( "G01*\n", m_outputFile );
301
302 // Add aperture list start point
303 fputs( "G04 APERTURE LIST*\n", m_outputFile );
304
305 // Give a minimal value to the default pen size, used to plot items in sketch mode
306 if( m_renderSettings )
307 {
308 const int pen_min = 0.1 * m_IUsPerDecimil * 10000 / 25.4; // for min width = 0.1 mm
309 m_renderSettings->SetDefaultPenWidth( std::max( m_renderSettings->GetDefaultPenWidth(),
310 pen_min ) );
311 }
312
313 return true;
314 }
315
316
EndPlot()317 bool GERBER_PLOTTER::EndPlot()
318 {
319 char line[1024];
320 wxString msg;
321
322 wxASSERT( m_outputFile );
323
324 /* Outfile is actually a temporary file i.e. workFile */
325 fputs( "M02*\n", m_outputFile );
326 fflush( m_outputFile );
327
328 fclose( workFile );
329 workFile = wxFopen( m_workFilename, wxT( "rt" ));
330 wxASSERT( workFile );
331 m_outputFile = finalFile;
332
333 // Placement of apertures in RS274X
334 while( fgets( line, 1024, workFile ) )
335 {
336 fputs( line, m_outputFile );
337
338 char* substr = strtok( line, "\n\r" );
339
340 if( substr && strcmp( substr, "G04 APERTURE LIST*" ) == 0 )
341 {
342 // Add aperture list macro:
343 if( m_hasApertureRoundRect | m_hasApertureRotOval ||
344 m_hasApertureOutline4P || m_hasApertureRotRect ||
345 m_hasApertureChamferedRect || m_am_freepoly_list.AmCount() )
346 {
347 fputs( "G04 Aperture macros list*\n", m_outputFile );
348
349 if( m_hasApertureRoundRect )
350 fputs( APER_MACRO_ROUNDRECT_HEADER, m_outputFile );
351
352 if( m_hasApertureRotOval )
353 fputs( APER_MACRO_SHAPE_OVAL_HEADER, m_outputFile );
354
355 if( m_hasApertureRotRect )
356 fputs( APER_MACRO_ROT_RECT_HEADER, m_outputFile );
357
358 if( m_hasApertureOutline4P )
359 fputs( APER_MACRO_OUTLINE4P_HEADER, m_outputFile );
360
361 if( m_hasApertureChamferedRect )
362 {
363 fputs( APER_MACRO_OUTLINE5P_HEADER, m_outputFile );
364 fputs( APER_MACRO_OUTLINE6P_HEADER, m_outputFile );
365 fputs( APER_MACRO_OUTLINE7P_HEADER, m_outputFile );
366 fputs( APER_MACRO_OUTLINE8P_HEADER, m_outputFile );
367 }
368
369 if( m_am_freepoly_list.AmCount() )
370 {
371 // aperture sizes are in inch or mm, regardless the
372 // coordinates format
373 double fscale = 0.0001 * m_plotScale / m_IUsPerDecimil; // inches
374
375 if(! m_gerberUnitInch )
376 fscale *= 25.4; // size in mm
377
378 m_am_freepoly_list.Format( m_outputFile, fscale );
379 }
380
381 fputs( "G04 Aperture macros list end*\n", m_outputFile );
382 }
383
384 writeApertureList();
385 fputs( "G04 APERTURE END LIST*\n", m_outputFile );
386 }
387 }
388
389 fclose( workFile );
390 fclose( finalFile );
391 ::wxRemoveFile( m_workFilename );
392 m_outputFile = nullptr;
393
394 return true;
395 }
396
397
SetCurrentLineWidth(int aWidth,void * aData)398 void GERBER_PLOTTER::SetCurrentLineWidth( int aWidth, void* aData )
399 {
400 if( aWidth == DO_NOT_SET_LINE_WIDTH )
401 return;
402 else if( aWidth == USE_DEFAULT_LINE_WIDTH )
403 aWidth = m_renderSettings->GetDefaultPenWidth();
404
405 wxASSERT_MSG( aWidth >= 0, "Plotter called to set negative pen width" );
406
407 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
408 int aperture_attribute = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
409
410 selectAperture( wxSize( aWidth, aWidth ), 0, 0.0, APERTURE::AT_PLOTTING, aperture_attribute );
411 m_currentPenWidth = aWidth;
412 }
413
414
GetOrCreateAperture(const wxSize & aSize,int aRadius,double aRotDegree,APERTURE::APERTURE_TYPE aType,int aApertureAttribute)415 int GERBER_PLOTTER::GetOrCreateAperture( const wxSize& aSize, int aRadius, double aRotDegree,
416 APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
417 {
418 int last_D_code = 9;
419
420 // Search an existing aperture
421 for( int idx = 0; idx < (int)m_apertures.size(); ++idx )
422 {
423 APERTURE* tool = &m_apertures[idx];
424 last_D_code = tool->m_DCode;
425
426 if( (tool->m_Type == aType) && (tool->m_Size == aSize) &&
427 (tool->m_Radius == aRadius) && (tool->m_Rotation == aRotDegree) &&
428 (tool->m_ApertureAttribute == aApertureAttribute) )
429 return idx;
430 }
431
432 // Allocate a new aperture
433 APERTURE new_tool;
434 new_tool.m_Size = aSize;
435 new_tool.m_Type = aType;
436 new_tool.m_Radius = aRadius;
437 new_tool.m_Rotation = aRotDegree;
438 new_tool.m_DCode = last_D_code + 1;
439 new_tool.m_ApertureAttribute = aApertureAttribute;
440
441 m_apertures.push_back( new_tool );
442
443 return m_apertures.size() - 1;
444 }
445
446
GetOrCreateAperture(const std::vector<wxPoint> & aCorners,double aRotDegree,APERTURE::APERTURE_TYPE aType,int aApertureAttribute)447 int GERBER_PLOTTER::GetOrCreateAperture( const std::vector<wxPoint>& aCorners, double aRotDegree,
448 APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
449 {
450 int last_D_code = 9;
451
452 // For APERTURE::AM_FREE_POLYGON aperture macros, we need to create the macro
453 // on the fly, because due to the fact the vertex count is not a constant we
454 // cannot create a static definition.
455 if( APERTURE::AM_FREE_POLYGON == aType )
456 {
457 int idx = m_am_freepoly_list.FindAm( aCorners );
458
459 if( idx < 0 )
460 m_am_freepoly_list.Append( aCorners );
461 }
462
463 // Search an existing aperture
464 for( int idx = 0; idx < (int)m_apertures.size(); ++idx )
465 {
466 APERTURE* tool = &m_apertures[idx];
467
468 last_D_code = tool->m_DCode;
469
470 if( (tool->m_Type == aType) &&
471 (tool->m_Corners.size() == aCorners.size() ) &&
472 (tool->m_Rotation == aRotDegree) &&
473 (tool->m_ApertureAttribute == aApertureAttribute) )
474 {
475 // A candidate is found. the corner lists must be similar
476 bool is_same = polyCompare( tool->m_Corners, aCorners );
477
478 if( is_same )
479 return idx;
480 }
481 }
482
483 // Allocate a new aperture
484 APERTURE new_tool;
485
486 new_tool.m_Corners = aCorners;
487 new_tool.m_Size = wxSize( 0, 0 ); // Not used
488 new_tool.m_Type = aType;
489 new_tool.m_Radius = 0; // Not used
490 new_tool.m_Rotation = aRotDegree;
491 new_tool.m_DCode = last_D_code + 1;
492 new_tool.m_ApertureAttribute = aApertureAttribute;
493
494 m_apertures.push_back( new_tool );
495
496 return m_apertures.size() - 1;
497 }
498
499
selectAperture(const wxSize & aSize,int aRadius,double aRotDegree,APERTURE::APERTURE_TYPE aType,int aApertureAttribute)500 void GERBER_PLOTTER::selectAperture( const wxSize& aSize, int aRadius, double aRotDegree,
501 APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
502 {
503 bool change = ( m_currentApertureIdx < 0 ) ||
504 ( m_apertures[m_currentApertureIdx].m_Type != aType ) ||
505 ( m_apertures[m_currentApertureIdx].m_Size != aSize ) ||
506 ( m_apertures[m_currentApertureIdx].m_Radius != aRadius ) ||
507 ( m_apertures[m_currentApertureIdx].m_Rotation != aRotDegree );
508
509 if( !change )
510 change = m_apertures[m_currentApertureIdx].m_ApertureAttribute != aApertureAttribute;
511
512 if( change )
513 {
514 // Pick an existing aperture or create a new one
515 m_currentApertureIdx = GetOrCreateAperture( aSize, aRadius, aRotDegree,
516 aType, aApertureAttribute );
517 fprintf( m_outputFile, "D%d*\n", m_apertures[m_currentApertureIdx].m_DCode );
518 }
519 }
520
521
selectAperture(const std::vector<wxPoint> & aCorners,double aRotDegree,APERTURE::APERTURE_TYPE aType,int aApertureAttribute)522 void GERBER_PLOTTER::selectAperture( const std::vector<wxPoint>& aCorners, double aRotDegree,
523 APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
524 {
525 bool change = ( m_currentApertureIdx < 0 ) ||
526 ( m_apertures[m_currentApertureIdx].m_Type != aType ) ||
527 ( m_apertures[m_currentApertureIdx].m_Corners.size() != aCorners.size() ) ||
528 ( m_apertures[m_currentApertureIdx].m_Rotation != aRotDegree );
529
530 if( !change ) // Compare corner lists
531 {
532 for( size_t ii = 0; ii < aCorners.size(); ii++ )
533 {
534 if( aCorners[ii] != m_apertures[m_currentApertureIdx].m_Corners[ii] )
535 {
536 change = true;
537 break;
538 }
539 }
540 }
541
542 if( !change )
543 change = m_apertures[m_currentApertureIdx].m_ApertureAttribute != aApertureAttribute;
544
545 if( change )
546 {
547 // Pick an existing aperture or create a new one
548 m_currentApertureIdx = GetOrCreateAperture( aCorners, aRotDegree,
549 aType, aApertureAttribute );
550 fprintf( m_outputFile, "D%d*\n", m_apertures[m_currentApertureIdx].m_DCode );
551 }
552 }
553
554
selectAperture(int aDiameter,double aPolygonRotation,APERTURE::APERTURE_TYPE aType,int aApertureAttribute)555 void GERBER_PLOTTER::selectAperture( int aDiameter, double aPolygonRotation,
556 APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
557 {
558 // Pick an existing aperture or create a new one, matching the
559 // aDiameter, aPolygonRotation, type and attributes for type =
560 // AT_REGULAR_POLY3 to AT_REGULAR_POLY12
561
562 wxASSERT( aType>= APERTURE::APERTURE_TYPE::AT_REGULAR_POLY3 &&
563 aType <= APERTURE::APERTURE_TYPE::AT_REGULAR_POLY12 );
564
565 wxSize size( aDiameter, (int)( aPolygonRotation * 1000.0 ) );
566 selectAperture( wxSize( 0, 0), aDiameter/2, aPolygonRotation, aType, aApertureAttribute );
567 }
568
writeApertureList()569 void GERBER_PLOTTER::writeApertureList()
570 {
571 wxASSERT( m_outputFile );
572 char cbuf[1024];
573 std::string buffer;
574
575 bool useX1StructuredComment = false;
576
577 if( !m_useX2format )
578 useX1StructuredComment = true;
579
580 // Init
581 for( APERTURE& tool : m_apertures )
582 {
583 // aperture sizes are in inch or mm, regardless the
584 // coordinates format
585 double fscale = 0.0001 * m_plotScale / m_IUsPerDecimil; // inches
586
587 if(! m_gerberUnitInch )
588 fscale *= 25.4; // size in mm
589
590 int attribute = tool.m_ApertureAttribute;
591
592 if( attribute != m_apertureAttribute )
593 {
594 fputs( GBR_APERTURE_METADATA::FormatAttribute(
595 (GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB) attribute,
596 useX1StructuredComment ).c_str(), m_outputFile );
597 }
598
599 sprintf( cbuf, "%%ADD%d", tool.m_DCode );
600 buffer = cbuf;
601
602 /* Please note: the Gerber specs for mass parameters say that
603 exponential syntax is *not* allowed and the decimal point should
604 also be always inserted. So the %g format is ruled out, but %f is fine
605 (the # modifier forces the decimal point). Sadly the %f formatter
606 can't remove trailing zeros but that's not a problem, since nothing
607 forbid it (the file is only slightly longer) */
608
609 switch( tool.m_Type )
610 {
611 case APERTURE::AT_CIRCLE:
612 sprintf( cbuf, "C,%#f*%%\n", tool.GetDiameter() * fscale );
613 break;
614
615 case APERTURE::AT_RECT:
616 sprintf( cbuf, "R,%#fX%#f*%%\n", tool.m_Size.x * fscale,
617 tool.m_Size.y * fscale );
618 break;
619
620 case APERTURE::AT_PLOTTING:
621 sprintf( cbuf, "C,%#f*%%\n", tool.m_Size.x * fscale );
622 break;
623
624 case APERTURE::AT_OVAL:
625 sprintf( cbuf, "O,%#fX%#f*%%\n", tool.m_Size.x * fscale,
626 tool.m_Size.y * fscale );
627 break;
628
629 case APERTURE::AT_REGULAR_POLY:
630 case APERTURE::AT_REGULAR_POLY3:
631 case APERTURE::AT_REGULAR_POLY4:
632 case APERTURE::AT_REGULAR_POLY5:
633 case APERTURE::AT_REGULAR_POLY6:
634 case APERTURE::AT_REGULAR_POLY7:
635 case APERTURE::AT_REGULAR_POLY8:
636 case APERTURE::AT_REGULAR_POLY9:
637 case APERTURE::AT_REGULAR_POLY10:
638 case APERTURE::AT_REGULAR_POLY11:
639 case APERTURE::AT_REGULAR_POLY12:
640 sprintf( cbuf, "P,%#fX%dX%#f*%%\n", tool.GetDiameter() * fscale,
641 tool.GetRegPolyVerticeCount(), tool.GetRotation() );
642 break;
643
644 case APERTURE::AM_ROUND_RECT: // Aperture macro for round rect pads
645 {
646 // The aperture macro needs coordinates of the centers of the 4 corners
647 std::vector<VECTOR2I> corners;
648 wxSize half_size( tool.m_Size.x/2-tool.m_Radius, tool.m_Size.y/2-tool.m_Radius );
649
650 corners.emplace_back( -half_size.x, -half_size.y );
651 corners.emplace_back( half_size.x, -half_size.y );
652 corners.emplace_back( half_size.x, half_size.y );
653 corners.emplace_back( -half_size.x, half_size.y );
654
655 // Rotate the corner coordinates:
656 for( int ii = 0; ii < 4; ii++ )
657 RotatePoint( corners[ii], -tool.m_Rotation*10.0 );
658
659 sprintf( cbuf, "%s,%#fX", APER_MACRO_ROUNDRECT_NAME,
660 tool.m_Radius * fscale );
661 buffer += cbuf;
662
663 // Add each corner
664 for( int ii = 0; ii < 4; ii++ )
665 {
666 sprintf( cbuf, "%#fX%#fX",
667 corners[ii].x * fscale, corners[ii].y * fscale );
668 buffer += cbuf;
669 }
670
671 sprintf( cbuf, "0*%%\n" );
672 }
673 break;
674
675 case APERTURE::AM_ROT_RECT: // Aperture macro for rotated rect pads
676 sprintf( cbuf, "%s,%#fX%#fX%#f*%%\n", APER_MACRO_ROT_RECT_NAME,
677 tool.m_Size.x * fscale, tool.m_Size.y * fscale,
678 tool.m_Rotation );
679 break;
680
681 case APERTURE::APER_MACRO_OUTLINE4P: // Aperture macro for trapezoid pads
682 case APERTURE::APER_MACRO_OUTLINE5P: // Aperture macro for chamfered rect pads
683 case APERTURE::APER_MACRO_OUTLINE6P: // Aperture macro for chamfered rect pads
684 case APERTURE::APER_MACRO_OUTLINE7P: // Aperture macro for chamfered rect pads
685 case APERTURE::APER_MACRO_OUTLINE8P: // Aperture macro for chamfered rect pads
686 switch( tool.m_Type )
687 {
688 case APERTURE::APER_MACRO_OUTLINE4P:
689 sprintf( cbuf, "%s,", APER_MACRO_OUTLINE4P_NAME ); break;
690 case APERTURE::APER_MACRO_OUTLINE5P:
691 sprintf( cbuf, "%s,", APER_MACRO_OUTLINE5P_NAME ); break;
692 case APERTURE::APER_MACRO_OUTLINE6P:
693 sprintf( cbuf, "%s,", APER_MACRO_OUTLINE6P_NAME ); break;
694 case APERTURE::APER_MACRO_OUTLINE7P:
695 sprintf( cbuf, "%s,", APER_MACRO_OUTLINE7P_NAME ); break;
696 case APERTURE::APER_MACRO_OUTLINE8P:
697 sprintf( cbuf, "%s,", APER_MACRO_OUTLINE8P_NAME ); break;
698 default:
699 break;
700 }
701
702 buffer += cbuf;
703
704 // Output all corners (should be 4 to 8 corners)
705 // Remember: the Y coordinate must be negated, due to the fact in Pcbnew
706 // the Y axis is from top to bottom
707 for( size_t ii = 0; ii < tool.m_Corners.size(); ii++ )
708 {
709 sprintf( cbuf, "%#fX%#fX",
710 tool.m_Corners[ii].x * fscale, -tool.m_Corners[ii].y * fscale );
711 buffer += cbuf;
712 }
713
714 // close outline and output rotation
715 sprintf( cbuf, "%#f*%%\n", tool.m_Rotation );
716 break;
717
718 case APERTURE::AM_ROTATED_OVAL: // Aperture macro for rotated oval pads
719 // (not rotated is a primitive)
720 // m_Size.x = full length; m_Size.y = width, and the macro aperture expects
721 // the position of ends
722 {
723 // the seg_len is the distance between the 2 circle centers
724 int seg_len = tool.m_Size.x - tool.m_Size.y;
725 // Center of the circle on the segment start point:
726 VECTOR2I start( seg_len/2, 0 );
727 // Center of the circle on the segment end point:
728 VECTOR2I end( - seg_len/2, 0 );
729
730 RotatePoint( start, tool.m_Rotation*10.0 );
731 RotatePoint( end, tool.m_Rotation*10.0 );
732
733 sprintf( cbuf, "%s,%#fX%#fX%#fX%#fX%#fX0*%%\n", APER_MACRO_SHAPE_OVAL_NAME,
734 tool.m_Size.y * fscale, // width
735 start.x * fscale, -start.y * fscale, // X,Y corner start pos
736 end.x * fscale, -end.y * fscale ); // X,Y cornerend pos
737 }
738 break;
739
740 case APERTURE::AM_FREE_POLYGON:
741 {
742 // Find the aperture macro name in the list of aperture macro
743 // created on the fly for this polygon:
744 int idx = m_am_freepoly_list.FindAm( tool.m_Corners );
745
746 // Write DCODE id ( "%ADDxx" is already in buffer) and rotation
747 // the full line is something like :%ADD12FreePoly1,45.000000*%
748 sprintf( cbuf, "%s%d,%#f*%%\n", AM_FREEPOLY_BASENAME, idx, tool.m_Rotation );
749 break;
750 }
751 }
752
753 buffer += cbuf;
754 fputs( buffer.c_str(), m_outputFile );
755
756 m_apertureAttribute = attribute;
757
758 // Currently reset the aperture attribute. Perhaps a better optimization
759 // is to store the last attribute
760 if( attribute )
761 {
762 if( m_useX2format )
763 fputs( "%TD*%\n", m_outputFile );
764 else
765 fputs( "G04 #@! TD*\n", m_outputFile );
766
767 m_apertureAttribute = 0;
768 }
769
770 }
771 }
772
773
PenTo(const wxPoint & aPos,char plume)774 void GERBER_PLOTTER::PenTo( const wxPoint& aPos, char plume )
775 {
776 wxASSERT( m_outputFile );
777 DPOINT pos_dev = userToDeviceCoordinates( aPos );
778
779 switch( plume )
780 {
781 case 'Z':
782 break;
783
784 case 'U':
785 emitDcode( pos_dev, 2 );
786 break;
787
788 case 'D':
789 emitDcode( pos_dev, 1 );
790 }
791
792 m_penState = plume;
793 }
794
795
Rect(const wxPoint & p1,const wxPoint & p2,FILL_T fill,int width)796 void GERBER_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width )
797 {
798 std::vector< wxPoint > cornerList;
799
800 // Build corners list
801 cornerList.push_back( p1 );
802 wxPoint corner(p1.x, p2.y);
803 cornerList.push_back( corner );
804 cornerList.push_back( p2 );
805 corner.x = p2.x;
806 corner.y = p1.y;
807 cornerList.push_back( corner );
808 cornerList.push_back( p1 );
809
810 PlotPoly( cornerList, fill, width );
811 }
812
813
Circle(const wxPoint & aCenter,int aDiameter,FILL_T aFill,int aWidth)814 void GERBER_PLOTTER::Circle( const wxPoint& aCenter, int aDiameter, FILL_T aFill, int aWidth )
815 {
816 Arc( aCenter, 0, 3600, aDiameter / 2, aFill, aWidth );
817 }
818
819
820
Arc(const wxPoint & aCenter,double aStAngle,double aEndAngle,int aRadius,FILL_T aFill,int aWidth)821 void GERBER_PLOTTER::Arc( const wxPoint& aCenter, double aStAngle, double aEndAngle, int aRadius,
822 FILL_T aFill, int aWidth )
823 {
824 SetCurrentLineWidth( aWidth );
825
826 // aFill is not used here.
827 plotArc( aCenter, aStAngle, aEndAngle, aRadius, false );
828 }
829
830
Arc(const SHAPE_ARC & aArc)831 void GERBER_PLOTTER::Arc( const SHAPE_ARC& aArc )
832 {
833 SetCurrentLineWidth( aArc.GetWidth() );
834
835 // aFill is not used here.
836 plotArc( aArc, false );
837 }
838
839
plotArc(const SHAPE_ARC & aArc,bool aPlotInRegion)840 void GERBER_PLOTTER::plotArc( const SHAPE_ARC& aArc, bool aPlotInRegion )
841 {
842 wxPoint start( aArc.GetP0() );
843 wxPoint end( aArc.GetP1() );
844 wxPoint center( aArc.GetCenter() );
845 double start_angle = aArc.GetStartAngle();
846 double end_angle = aArc.GetEndAngle();
847
848 if( !aPlotInRegion )
849 MoveTo( start);
850 else
851 LineTo( start );
852
853 DPOINT devEnd = userToDeviceCoordinates( end );
854 DPOINT devCenter = userToDeviceCoordinates( center ) - userToDeviceCoordinates( start );
855
856 fprintf( m_outputFile, "G75*\n" ); // Multiquadrant (360 degrees) mode
857
858 if( start_angle < end_angle )
859 fprintf( m_outputFile, "G03*\n" ); // Active circular interpolation, CCW
860 else
861 fprintf( m_outputFile, "G02*\n" ); // Active circular interpolation, CW
862
863 fprintf( m_outputFile, "X%dY%dI%dJ%dD01*\n",
864 KiROUND( devEnd.x ), KiROUND( devEnd.y ),
865 KiROUND( devCenter.x ), KiROUND( devCenter.y ) );
866
867 fprintf( m_outputFile, "G01*\n" ); // Back to linear interpolate (perhaps useless here).
868 }
869
870
plotArc(const wxPoint & aCenter,double aStAngle,double aEndAngle,int aRadius,bool aPlotInRegion)871 void GERBER_PLOTTER::plotArc( const wxPoint& aCenter, double aStAngle, double aEndAngle,
872 int aRadius, bool aPlotInRegion )
873 {
874 wxPoint start, end;
875 start.x = aCenter.x + KiROUND( cosdecideg( aRadius, aStAngle ) );
876 start.y = aCenter.y - KiROUND( sindecideg( aRadius, aStAngle ) );
877
878 if( !aPlotInRegion )
879 MoveTo( start );
880 else
881 LineTo( start );
882
883 end.x = aCenter.x + KiROUND( cosdecideg( aRadius, aEndAngle ) );
884 end.y = aCenter.y - KiROUND( sindecideg( aRadius, aEndAngle ) );
885 DPOINT devEnd = userToDeviceCoordinates( end );
886 DPOINT devCenter = userToDeviceCoordinates( aCenter ) - userToDeviceCoordinates( start );
887
888 fprintf( m_outputFile, "G75*\n" ); // Multiquadrant (360 degrees) mode
889
890 if( aStAngle < aEndAngle )
891 fprintf( m_outputFile, "G03*\n" ); // Active circular interpolation, CCW
892 else
893 fprintf( m_outputFile, "G02*\n" ); // Active circular interpolation, CW
894
895 fprintf( m_outputFile, "X%dY%dI%dJ%dD01*\n",
896 KiROUND( devEnd.x ), KiROUND( devEnd.y ),
897 KiROUND( devCenter.x ), KiROUND( devCenter.y ) );
898
899 fprintf( m_outputFile, "G01*\n" ); // Back to linear interpolate (perhaps useless here).
900 }
901
902
PlotGerberRegion(const SHAPE_LINE_CHAIN & aPoly,void * aData)903 void GERBER_PLOTTER::PlotGerberRegion( const SHAPE_LINE_CHAIN& aPoly, void* aData )
904 {
905 if( aPoly.PointCount() <= 2 )
906 return;
907
908 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
909
910 bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
911
912 if( gbr_metadata )
913 {
914 std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
915
916 if( !attrib.empty() )
917 {
918 fputs( attrib.c_str(), m_outputFile );
919 clearTA_AperFunction = true;
920 }
921 }
922
923 PlotPoly( aPoly, FILL_T::FILLED_SHAPE, 0 , gbr_metadata );
924
925 // Clear the TA attribute, to avoid the next item to inherit it:
926 if( clearTA_AperFunction )
927 {
928 if( m_useX2format )
929 {
930 fputs( "%TD.AperFunction*%\n", m_outputFile );
931 }
932 else
933 {
934 fputs( "G04 #@! TD.AperFunction*\n", m_outputFile );
935 }
936 }
937 }
938
939
PlotGerberRegion(const std::vector<wxPoint> & aCornerList,void * aData)940 void GERBER_PLOTTER::PlotGerberRegion( const std::vector< wxPoint >& aCornerList, void* aData )
941 {
942 if( aCornerList.size() <= 2 )
943 return;
944
945 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
946
947 bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
948
949 if( gbr_metadata )
950 {
951 std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
952
953 if( !attrib.empty() )
954 {
955 fputs( attrib.c_str(), m_outputFile );
956 clearTA_AperFunction = true;
957 }
958 }
959
960 PlotPoly( aCornerList, FILL_T::FILLED_SHAPE, 0, gbr_metadata );
961
962 // Clear the TA attribute, to avoid the next item to inherit it:
963 if( clearTA_AperFunction )
964 {
965 if( m_useX2format )
966 {
967 fputs( "%TD.AperFunction*%\n", m_outputFile );
968 }
969 else
970 {
971 fputs( "G04 #@! TD.AperFunction*\n", m_outputFile );
972 }
973 }
974 }
975
976
PlotPoly(const SHAPE_LINE_CHAIN & aPoly,FILL_T aFill,int aWidth,void * aData)977 void GERBER_PLOTTER::PlotPoly( const SHAPE_LINE_CHAIN& aPoly, FILL_T aFill, int aWidth,
978 void* aData )
979 {
980 if( aPoly.CPoints().size() <= 1 )
981 return;
982
983 // Gerber format does not know filled polygons with thick outline
984 // Therefore, to plot a filled polygon with outline having a thickness,
985 // one should plot outline as thick segments
986 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
987
988 if( gbr_metadata )
989 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
990
991 if( aFill != FILL_T::NO_FILL )
992 {
993 fputs( "G36*\n", m_outputFile );
994
995 MoveTo( wxPoint( aPoly.CPoint( 0 ) ) );
996
997 fputs( "G01*\n", m_outputFile ); // Set linear interpolation.
998
999 for( int ii = 1; ii < aPoly.PointCount(); ii++ )
1000 {
1001 int arcindex = aPoly.ArcIndex( ii );
1002
1003 if( arcindex < 0 )
1004 {
1005 /// Plain point
1006 LineTo( wxPoint( aPoly.CPoint( ii ) ) );
1007 }
1008 else
1009 {
1010 const SHAPE_ARC& arc = aPoly.Arc( arcindex );
1011
1012 plotArc( arc, ii > 0 );
1013 }
1014 }
1015
1016 // If the polygon is not closed, close it:
1017 if( aPoly.CPoint( 0 ) != aPoly.CPoint( -1 ) )
1018 FinishTo( wxPoint( aPoly.CPoint( 0 ) ) );
1019
1020 fputs( "G37*\n", m_outputFile );
1021 }
1022
1023 if( aWidth > 0 ) // Draw the polyline/polygon outline
1024 {
1025 SetCurrentLineWidth( aWidth, gbr_metadata );
1026
1027 MoveTo( wxPoint( aPoly.CPoint( 0 ) ) );
1028
1029 for( int ii = 1; ii < aPoly.PointCount(); ii++ )
1030 {
1031 int arcindex = aPoly.ArcIndex( ii );
1032
1033 if( arcindex < 0 )
1034 {
1035 /// Plain point
1036 LineTo( wxPoint( aPoly.CPoint( ii ) ) );
1037 }
1038 else
1039 {
1040 const SHAPE_ARC& arc = aPoly.Arc( arcindex );
1041
1042 plotArc( arc, ii > 0 );
1043 }
1044 }
1045
1046 // Ensure the thick outline is closed for filled polygons
1047 // (if not filled, could be only a polyline)
1048 if( aFill != FILL_T::NO_FILL && ( aPoly.CPoint( 0 ) != aPoly.CPoint( -1 ) ) )
1049 LineTo( wxPoint( aPoly.CPoint( 0 ) ) );
1050
1051 PenFinish();
1052 }
1053 }
1054
PlotPoly(const std::vector<wxPoint> & aCornerList,FILL_T aFill,int aWidth,void * aData)1055 void GERBER_PLOTTER::PlotPoly( const std::vector< wxPoint >& aCornerList, FILL_T aFill, int aWidth,
1056 void * aData )
1057 {
1058 if( aCornerList.size() <= 1 )
1059 return;
1060
1061 // Gerber format does not know filled polygons with thick outline
1062 // Therefore, to plot a filled polygon with outline having a thickness,
1063 // one should plot outline as thick segments
1064 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1065
1066 if( gbr_metadata )
1067 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1068
1069 if( aFill != FILL_T::NO_FILL )
1070 {
1071 fputs( "G36*\n", m_outputFile );
1072
1073 MoveTo( aCornerList[0] );
1074 fputs( "G01*\n", m_outputFile ); // Set linear interpolation.
1075
1076 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1077 LineTo( aCornerList[ii] );
1078
1079 // If the polygon is not closed, close it:
1080 if( aCornerList[0] != aCornerList[aCornerList.size()-1] )
1081 FinishTo( aCornerList[0] );
1082
1083 fputs( "G37*\n", m_outputFile );
1084 }
1085
1086 if( aWidth > 0 ) // Draw the polyline/polygon outline
1087 {
1088 SetCurrentLineWidth( aWidth, gbr_metadata );
1089
1090 MoveTo( aCornerList[0] );
1091
1092 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1093 LineTo( aCornerList[ii] );
1094
1095 // Ensure the thick outline is closed for filled polygons
1096 // (if not filled, could be only a polyline)
1097 if( aFill != FILL_T::NO_FILL && ( aCornerList[aCornerList.size() - 1] != aCornerList[0] ) )
1098 LineTo( aCornerList[0] );
1099
1100 PenFinish();
1101 }
1102 }
1103
1104
ThickSegment(const wxPoint & start,const wxPoint & end,int width,OUTLINE_MODE tracemode,void * aData)1105 void GERBER_PLOTTER::ThickSegment( const wxPoint& start, const wxPoint& end, int width,
1106 OUTLINE_MODE tracemode, void* aData )
1107 {
1108 if( tracemode == FILLED )
1109 {
1110 GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1111 SetCurrentLineWidth( width, gbr_metadata );
1112
1113 if( gbr_metadata )
1114 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1115
1116 MoveTo( start );
1117 FinishTo( end );
1118 }
1119 else
1120 {
1121 SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH );
1122 segmentAsOval( start, end, width, tracemode );
1123 }
1124 }
1125
ThickArc(const wxPoint & centre,double StAngle,double EndAngle,int radius,int width,OUTLINE_MODE tracemode,void * aData)1126 void GERBER_PLOTTER::ThickArc( const wxPoint& centre, double StAngle, double EndAngle,
1127 int radius, int width, OUTLINE_MODE tracemode, void* aData )
1128 {
1129 GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1130 SetCurrentLineWidth( width, gbr_metadata );
1131
1132 if( gbr_metadata )
1133 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1134
1135 if( tracemode == FILLED )
1136 {
1137 Arc( centre, StAngle, EndAngle, radius, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1138 }
1139 else
1140 {
1141 SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH );
1142 Arc( centre, StAngle, EndAngle, radius - ( width - m_currentPenWidth ) / 2, FILL_T::NO_FILL,
1143 DO_NOT_SET_LINE_WIDTH );
1144 Arc( centre, StAngle, EndAngle, radius + ( width - m_currentPenWidth ) / 2, FILL_T::NO_FILL,
1145 DO_NOT_SET_LINE_WIDTH );
1146 }
1147 }
1148
1149
ThickRect(const wxPoint & p1,const wxPoint & p2,int width,OUTLINE_MODE tracemode,void * aData)1150 void GERBER_PLOTTER::ThickRect( const wxPoint& p1, const wxPoint& p2, int width,
1151 OUTLINE_MODE tracemode, void* aData )
1152 {
1153 GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1154 SetCurrentLineWidth( width, gbr_metadata );
1155
1156 if( gbr_metadata )
1157 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1158
1159 if( tracemode == FILLED )
1160 {
1161 Rect( p1, p2, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1162 }
1163 else
1164 {
1165 SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH );
1166 wxPoint offsetp1( p1.x - (width - m_currentPenWidth) / 2,
1167 p1.y - (width - m_currentPenWidth) / 2 );
1168 wxPoint offsetp2( p2.x + (width - m_currentPenWidth) / 2,
1169 p2.y + (width - m_currentPenWidth) / 2 );
1170 Rect( offsetp1, offsetp2, FILL_T::NO_FILL, -1 );
1171 offsetp1.x += (width - m_currentPenWidth);
1172 offsetp1.y += (width - m_currentPenWidth);
1173 offsetp2.x -= (width - m_currentPenWidth);
1174 offsetp2.y -= (width - m_currentPenWidth);
1175 Rect( offsetp1, offsetp2, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1176 }
1177 }
1178
1179
ThickCircle(const wxPoint & pos,int diametre,int width,OUTLINE_MODE tracemode,void * aData)1180 void GERBER_PLOTTER::ThickCircle( const wxPoint& pos, int diametre, int width,
1181 OUTLINE_MODE tracemode, void* aData )
1182 {
1183 GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1184 SetCurrentLineWidth( width, gbr_metadata );
1185
1186 if( gbr_metadata )
1187 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1188
1189 if( tracemode == FILLED )
1190 {
1191 Circle( pos, diametre, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1192 }
1193 else
1194 {
1195 SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, gbr_metadata );
1196 Circle( pos, diametre - (width - m_currentPenWidth), FILL_T::NO_FILL,
1197 DO_NOT_SET_LINE_WIDTH );
1198 Circle( pos, diametre + (width - m_currentPenWidth), FILL_T::NO_FILL,
1199 DO_NOT_SET_LINE_WIDTH );
1200 }
1201 }
1202
1203
FilledCircle(const wxPoint & pos,int diametre,OUTLINE_MODE tracemode,void * aData)1204 void GERBER_PLOTTER::FilledCircle( const wxPoint& pos, int diametre,
1205 OUTLINE_MODE tracemode, void* aData )
1206 {
1207 // A filled circle is a graphic item, not a pad.
1208 // So it is drawn, not flashed.
1209 GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1210
1211 if( gbr_metadata )
1212 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1213
1214 if( tracemode == FILLED )
1215 {
1216 // Draw a circle of diameter = diameter/2 with a line thickness = radius,
1217 // To create a filled circle
1218 SetCurrentLineWidth( diametre/2, gbr_metadata );
1219 Circle( pos, diametre/2, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1220 }
1221 else
1222 {
1223 SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, gbr_metadata );
1224 Circle( pos, diametre, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1225 }
1226 }
1227
1228
FlashPadCircle(const wxPoint & pos,int diametre,OUTLINE_MODE trace_mode,void * aData)1229 void GERBER_PLOTTER::FlashPadCircle( const wxPoint& pos, int diametre, OUTLINE_MODE trace_mode,
1230 void* aData )
1231 {
1232 wxSize size( diametre, diametre );
1233 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1234
1235 if( trace_mode == SKETCH )
1236 {
1237 if( gbr_metadata )
1238 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1239
1240 SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH );
1241
1242 Circle( pos, diametre - m_currentPenWidth, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1243 }
1244 else
1245 {
1246 DPOINT pos_dev = userToDeviceCoordinates( pos );
1247
1248 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1249 selectAperture( size, 0, 0.0, APERTURE::AT_CIRCLE, aperture_attrib );
1250
1251 if( gbr_metadata )
1252 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1253
1254 emitDcode( pos_dev, 3 );
1255 }
1256 }
1257
1258
FlashPadOval(const wxPoint & pos,const wxSize & aSize,double orient,OUTLINE_MODE trace_mode,void * aData)1259 void GERBER_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double orient,
1260 OUTLINE_MODE trace_mode, void* aData )
1261 {
1262 wxASSERT( m_outputFile );
1263 wxSize size( aSize );
1264 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1265
1266 // Flash a vertical or horizontal shape (this is a basic aperture).
1267 if( ( orient == 0 || orient == 900 || orient == 1800 || orient == 2700 )
1268 && trace_mode == FILLED )
1269 {
1270 if( orient == 900 || orient == 2700 ) /* orientation turned 90 deg. */
1271 std::swap( size.x, size.y );
1272
1273 DPOINT pos_dev = userToDeviceCoordinates( pos );
1274 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1275 selectAperture( size, 0, 0.0, APERTURE::AT_OVAL, aperture_attrib );
1276
1277 if( gbr_metadata )
1278 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1279
1280 emitDcode( pos_dev, 3 );
1281 }
1282 else // Plot pad as region.
1283 // Only regions and flashed items accept a object attribute TO.P for the pin name
1284 {
1285 if( trace_mode == FILLED )
1286 {
1287 #ifdef GBR_USE_MACROS_FOR_ROTATED_OVAL
1288 if( !m_gerberDisableApertMacros )
1289 #endif
1290 {
1291 m_hasApertureRotOval = true;
1292 // We are using a aperture macro that expect size.y < size.x
1293 // i.e draw a horizontal line for rotation = 0.0
1294 // size.x = length, size.y = width
1295 if( size.x < size.y )
1296 {
1297 std::swap( size.x, size.y );
1298 orient += 900;
1299
1300 if( orient > 1800 )
1301 orient -= 1800;
1302 }
1303
1304 DPOINT pos_dev = userToDeviceCoordinates( pos );
1305 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1306 selectAperture( size, 0, orient/10.0, APERTURE::AM_ROTATED_OVAL, aperture_attrib );
1307
1308 if( gbr_metadata )
1309 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1310
1311 emitDcode( pos_dev, 3 );
1312 return;
1313 }
1314 // Draw the oval as round rect pad with a radius = 50% min size)
1315 // In gerber file, it will be drawn as a region with arcs, and can be
1316 // detected as pads (similar to a flashed pad)
1317 FlashPadRoundRect( pos, aSize, std::min( aSize.x, aSize.y ) / 2,
1318 orient, FILLED, aData );
1319 }
1320 else // Non filled shape: plot outlines:
1321 {
1322 if( size.x > size.y )
1323 {
1324 std::swap( size.x, size.y );
1325
1326 if( orient < 2700 )
1327 orient += 900;
1328 else
1329 orient -= 2700;
1330 }
1331
1332 sketchOval( pos, size, orient, -1 );
1333 }
1334 }
1335 }
1336
1337
FlashPadRect(const wxPoint & pos,const wxSize & aSize,double orient,OUTLINE_MODE trace_mode,void * aData)1338 void GERBER_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& aSize,
1339 double orient, OUTLINE_MODE trace_mode, void* aData )
1340
1341 {
1342 wxASSERT( m_outputFile );
1343 wxSize size( aSize );
1344 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1345
1346 // Plot as an aperture flash
1347 switch( int( orient ) )
1348 {
1349 case 900:
1350 case 2700: // rotation of 90 degrees or 270 swaps sizes
1351 std::swap( size.x, size.y );
1352 KI_FALLTHROUGH;
1353
1354 case 0:
1355 case 1800:
1356 if( trace_mode == SKETCH )
1357 {
1358 if( gbr_metadata )
1359 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1360
1361 SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH );
1362 Rect( wxPoint( pos.x - (size.x - GetCurrentLineWidth()) / 2,
1363 pos.y - (size.y - GetCurrentLineWidth()) / 2 ),
1364 wxPoint( pos.x + (size.x - GetCurrentLineWidth()) / 2,
1365 pos.y + (size.y - GetCurrentLineWidth()) / 2 ),
1366 FILL_T::NO_FILL, GetCurrentLineWidth() );
1367 }
1368 else
1369 {
1370 DPOINT pos_dev = userToDeviceCoordinates( pos );
1371 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1372 selectAperture( size, 0, 0.0, APERTURE::AT_RECT, aperture_attrib );
1373
1374 if( gbr_metadata )
1375 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1376
1377 emitDcode( pos_dev, 3 );
1378 }
1379 break;
1380
1381 default:
1382 #ifdef GBR_USE_MACROS_FOR_ROTATED_RECT
1383 if( trace_mode != SKETCH && !m_gerberDisableApertMacros )
1384 {
1385 m_hasApertureRotRect = true;
1386 DPOINT pos_dev = userToDeviceCoordinates( pos );
1387 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1388 selectAperture( size, 0, orient/10.0, APERTURE::AM_ROT_RECT, aperture_attrib );
1389
1390 if( gbr_metadata )
1391 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1392
1393 emitDcode( pos_dev, 3 );
1394
1395 break;
1396 }
1397 #endif
1398 {
1399 // plot pad shape as Gerber region
1400 wxPoint coord[4];
1401 // coord[0] is assumed the lower left
1402 // coord[1] is assumed the upper left
1403 // coord[2] is assumed the upper right
1404 // coord[3] is assumed the lower right
1405
1406 coord[0].x = -size.x/2; // lower left
1407 coord[0].y = size.y/2;
1408 coord[1].x = -size.x/2; // upper left
1409 coord[1].y = -size.y/2;
1410 coord[2].x = size.x/2; // upper right
1411 coord[2].y = -size.y/2;
1412 coord[3].x = size.x/2; // lower right
1413 coord[3].y = size.y/2;
1414
1415 FlashPadTrapez( pos, coord, orient, trace_mode, aData );
1416 }
1417 break;
1418 }
1419 }
1420
FlashPadRoundRect(const wxPoint & aPadPos,const wxSize & aSize,int aCornerRadius,double aOrient,OUTLINE_MODE aTraceMode,void * aData)1421 void GERBER_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSize,
1422 int aCornerRadius, double aOrient,
1423 OUTLINE_MODE aTraceMode, void* aData )
1424
1425 {
1426 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1427
1428 if( aTraceMode != FILLED )
1429 {
1430 SHAPE_POLY_SET outline;
1431 TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient, aCornerRadius,
1432 0.0, 0, 0, GetPlotterArcHighDef(), ERROR_INSIDE );
1433
1434 SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata );
1435 outline.Inflate( -GetCurrentLineWidth()/2, 16 );
1436
1437 std::vector< wxPoint > cornerList;
1438 // TransformRoundRectToPolygon creates only one convex polygon
1439 SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
1440 cornerList.reserve( poly.PointCount() + 1 );
1441
1442 for( int ii = 0; ii < poly.PointCount(); ++ii )
1443 cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
1444
1445 // Close polygon
1446 cornerList.push_back( cornerList[0] );
1447
1448 // plot outlines
1449 PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), gbr_metadata );
1450 }
1451 else
1452 {
1453 #ifdef GBR_USE_MACROS_FOR_ROUNDRECT
1454 if( !m_gerberDisableApertMacros )
1455 #endif
1456 {
1457 m_hasApertureRoundRect = true;
1458
1459 DPOINT pos_dev = userToDeviceCoordinates( aPadPos );
1460 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1461 selectAperture( aSize, aCornerRadius, aOrient/10.0,
1462 APERTURE::AM_ROUND_RECT, aperture_attrib );
1463
1464 if( gbr_metadata )
1465 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1466
1467 emitDcode( pos_dev, 3 );
1468 return;
1469 }
1470
1471 // A Pad RoundRect is plotted as a Gerber region.
1472 // Initialize region metadata:
1473 bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
1474
1475 if( gbr_metadata )
1476 {
1477 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1478 std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
1479
1480 if( !attrib.empty() )
1481 {
1482 fputs( attrib.c_str(), m_outputFile );
1483 clearTA_AperFunction = true;
1484 }
1485 }
1486
1487 // Plot the region using arcs in corners.
1488 plotRoundRectAsRegion( aPadPos, aSize, aCornerRadius, aOrient );
1489
1490 // Clear the TA attribute, to avoid the next item to inherit it:
1491 if( clearTA_AperFunction )
1492 {
1493 if( m_useX2format )
1494 fputs( "%TD.AperFunction*%\n", m_outputFile );
1495 else
1496 fputs( "G04 #@! TD.AperFunction*\n", m_outputFile );
1497 }
1498 }
1499 }
1500
1501
plotRoundRectAsRegion(const wxPoint & aRectCenter,const wxSize & aSize,int aCornerRadius,double aOrient)1502 void GERBER_PLOTTER::plotRoundRectAsRegion( const wxPoint& aRectCenter, const wxSize& aSize,
1503 int aCornerRadius, double aOrient )
1504 {
1505 // The region outline is generated by 4 sides and 4 90 deg arcs
1506 // 1 --- 2
1507 // | c |
1508 // 4 --- 3
1509
1510 // Note also in user coordinates the Y axis is from top to bottom
1511 // for historical reasons.
1512
1513 // A helper structure to handle outlines coordinates (segments and arcs)
1514 // in user coordinates
1515 struct RR_EDGE
1516 {
1517 wxPoint m_start;
1518 wxPoint m_end;
1519 wxPoint m_center;
1520 // in decidegrees: angle start. angle end = m_arc_angle_start+arc_angle
1521 double m_arc_angle_start;
1522 };
1523
1524 const double arc_angle = -900.0; // in decidegrees
1525 int hsizeX = aSize.x/2;
1526 int hsizeY = aSize.y/2;
1527
1528 RR_EDGE curr_edge;
1529 std::vector<RR_EDGE> rr_outline;
1530
1531 // Build outline coordinates, relative to rectangle center, rotation 0:
1532
1533 // Top left corner 1 (and 4 to 1 left vertical side @ x=-hsizeX)
1534 curr_edge.m_start.x = -hsizeX;
1535 curr_edge.m_start.y = hsizeY - aCornerRadius;
1536 curr_edge.m_end.x = curr_edge.m_start.x;
1537 curr_edge.m_end.y = -hsizeY + aCornerRadius;
1538 curr_edge.m_center.x = -hsizeX + aCornerRadius;
1539 curr_edge.m_center.y = curr_edge.m_end.y;
1540 curr_edge.m_arc_angle_start = aOrient + 1800.0; // En decidegree
1541
1542 rr_outline.push_back( curr_edge );
1543
1544 // Top right corner 2 (and 1 to 2 top horizontal side @ y=-hsizeY)
1545 curr_edge.m_start.x = -hsizeX + aCornerRadius;
1546 curr_edge.m_start.y = -hsizeY;
1547 curr_edge.m_end.x = hsizeX - aCornerRadius;
1548 curr_edge.m_end.y = curr_edge.m_start.y;
1549 curr_edge.m_center.x = curr_edge.m_end.x;
1550 curr_edge.m_center.y = -hsizeY + aCornerRadius;
1551 curr_edge.m_arc_angle_start = aOrient + 900.0; // En decidegree
1552
1553 rr_outline.push_back( curr_edge );
1554
1555 // bottom right corner 3 (and 2 to 3 right vertical side @ x=hsizeX)
1556 curr_edge.m_start.x = hsizeX;
1557 curr_edge.m_start.y = -hsizeY + aCornerRadius;
1558 curr_edge.m_end.x = curr_edge.m_start.x;
1559 curr_edge.m_end.y = hsizeY - aCornerRadius;
1560 curr_edge.m_center.x = hsizeX - aCornerRadius;
1561 curr_edge.m_center.y = curr_edge.m_end.y;
1562 curr_edge.m_arc_angle_start = aOrient + 0.0; // En decidegree
1563
1564 rr_outline.push_back( curr_edge );
1565
1566 // bottom left corner 4 (and 3 to 4 bottom horizontal side @ y=hsizeY)
1567 curr_edge.m_start.x = hsizeX - aCornerRadius;
1568 curr_edge.m_start.y = hsizeY;
1569 curr_edge.m_end.x = -hsizeX + aCornerRadius;
1570 curr_edge.m_end.y = curr_edge.m_start.y;
1571 curr_edge.m_center.x = curr_edge.m_end.x;
1572 curr_edge.m_center.y = hsizeY - aCornerRadius;
1573 curr_edge.m_arc_angle_start = aOrient - 900.0; // En decidegree
1574
1575 rr_outline.push_back( curr_edge );
1576
1577 // Move relative coordinates to the actual location and rotation:
1578 wxPoint arc_last_center;
1579 int arc_last_angle = curr_edge.m_arc_angle_start+arc_angle;
1580
1581 for( RR_EDGE& rr_edge: rr_outline )
1582 {
1583 RotatePoint( &rr_edge.m_start, aOrient );
1584 RotatePoint( &rr_edge.m_end, aOrient );
1585 RotatePoint( &rr_edge.m_center, aOrient );
1586 rr_edge.m_start += aRectCenter;
1587 rr_edge.m_end += aRectCenter;
1588 rr_edge.m_center += aRectCenter;
1589 arc_last_center = rr_edge.m_center;
1590 }
1591
1592 // Ensure the region is a closed polygon, i.e. the end point of last segment
1593 // (end of arc) is the same as the first point. Rounding issues can create a
1594 // small difference, mainly for rotated pads.
1595 // calculate last point (end of last arc):
1596 wxPoint last_pt;
1597 last_pt.x = arc_last_center.x + KiROUND( cosdecideg( aCornerRadius, arc_last_angle ) );
1598 last_pt.y = arc_last_center.y - KiROUND( sindecideg( aCornerRadius, arc_last_angle ) );
1599
1600 wxPoint first_pt = rr_outline[0].m_start;
1601
1602 #if 0 // For test only:
1603 if( last_pt != first_pt )
1604 wxLogMessage( "first pt %d %d last pt %d %d",
1605 first_pt.x, first_pt.y, last_pt.x, last_pt.y );
1606 #endif
1607
1608 fputs( "G36*\n", m_outputFile ); // Start region
1609 fputs( "G01*\n", m_outputFile ); // Set linear interpolation.
1610 first_pt = last_pt;
1611 MoveTo( first_pt ); // Start point of region, must be same as end point
1612
1613 for( RR_EDGE& rr_edge: rr_outline )
1614 {
1615 if( aCornerRadius ) // Guard: ensure we do not create arcs with radius = 0
1616 {
1617 // LineTo( rr_edge.m_end ); // made in plotArc()
1618 plotArc( rr_edge.m_center, rr_edge.m_arc_angle_start,
1619 rr_edge.m_arc_angle_start+arc_angle, aCornerRadius, true );
1620 }
1621 else
1622 {
1623 LineTo( rr_edge.m_end );
1624 }
1625 }
1626
1627 fputs( "G37*\n", m_outputFile ); // Close region
1628 }
1629
1630
FlashPadCustom(const wxPoint & aPadPos,const wxSize & aSize,double aOrient,SHAPE_POLY_SET * aPolygons,OUTLINE_MODE aTraceMode,void * aData)1631 void GERBER_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize,
1632 double aOrient, SHAPE_POLY_SET* aPolygons,
1633 OUTLINE_MODE aTraceMode, void* aData )
1634
1635 {
1636 // A Pad custom is plotted as polygon (a region in Gerber language).
1637 GBR_METADATA gbr_metadata;
1638
1639 if( aData )
1640 gbr_metadata = *static_cast<GBR_METADATA*>( aData );
1641
1642 SHAPE_POLY_SET polyshape = *aPolygons;
1643
1644 if( aTraceMode != FILLED )
1645 {
1646 SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata );
1647 polyshape.Inflate( -GetCurrentLineWidth()/2, 16 );
1648 }
1649
1650 std::vector< wxPoint > cornerList;
1651
1652 for( int cnt = 0; cnt < polyshape.OutlineCount(); ++cnt )
1653 {
1654 SHAPE_LINE_CHAIN& poly = polyshape.Outline( cnt );
1655
1656 cornerList.clear();
1657
1658 for( int ii = 0; ii < poly.PointCount(); ++ii )
1659 cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
1660
1661 // Close polygon
1662 cornerList.push_back( cornerList[0] );
1663
1664 if( aTraceMode == SKETCH )
1665 {
1666 PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
1667 }
1668 else
1669 {
1670 #ifdef GBR_USE_MACROS_FOR_CUSTOM_PAD
1671 if( m_gerberDisableApertMacros
1672 || cornerList.size() > GBR_MACRO_FOR_CUSTOM_PAD_MAX_CORNER_COUNT )
1673 PlotGerberRegion( cornerList, &gbr_metadata );
1674 else
1675 {
1676 // An AM will be created. the shape must be in position 0,0 and orientation 0
1677 // to be able to reuse the same AM for pads having the same shape
1678 for( size_t ii = 0; ii < cornerList.size(); ii++ )
1679 {
1680 cornerList[ii] -= aPadPos;
1681 RotatePoint( &cornerList[ii], -aOrient );
1682 }
1683
1684 DPOINT pos_dev = userToDeviceCoordinates( aPadPos );
1685 selectAperture( cornerList, aOrient/10.0,
1686 APERTURE::AM_FREE_POLYGON, gbr_metadata.GetApertureAttrib() );
1687 formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1688
1689 emitDcode( pos_dev, 3 );
1690 }
1691 #else
1692 PlotGerberRegion( cornerList, &gbr_metadata );
1693 #endif
1694 }
1695 }
1696 }
1697
1698
FlashPadChamferRoundRect(const wxPoint & aShapePos,const wxSize & aPadSize,int aCornerRadius,double aChamferRatio,int aChamferPositions,double aPadOrient,OUTLINE_MODE aPlotMode,void * aData)1699 void GERBER_PLOTTER::FlashPadChamferRoundRect( const wxPoint& aShapePos, const wxSize& aPadSize,
1700 int aCornerRadius, double aChamferRatio,
1701 int aChamferPositions, double aPadOrient,
1702 OUTLINE_MODE aPlotMode, void* aData )
1703
1704 {
1705 GBR_METADATA gbr_metadata;
1706
1707 if( aData )
1708 gbr_metadata = *static_cast<GBR_METADATA*>( aData );
1709
1710 DPOINT pos_dev = userToDeviceCoordinates( aShapePos );
1711
1712 SHAPE_POLY_SET outline;
1713 // polygon corners list
1714 std::vector<wxPoint> cornerList;
1715
1716 bool hasRoundedCorner = aCornerRadius != 0 && aChamferPositions != 15;
1717
1718 #ifdef GBR_USE_MACROS_FOR_CHAMFERED_RECT
1719 // Sketch mode or round rect shape or Apert Macros disabled
1720 if( aPlotMode != FILLED || hasRoundedCorner || m_gerberDisableApertMacros )
1721 #endif
1722 {
1723 TransformRoundChamferedRectToPolygon( outline, aShapePos, aPadSize, aPadOrient,
1724 aCornerRadius, aChamferRatio, aChamferPositions, 0,
1725 GetPlotterArcHighDef(), ERROR_INSIDE );
1726
1727 // Build the corner list
1728 const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
1729
1730 for( int ii = 0; ii < corners.PointCount(); ii++ )
1731 cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
1732
1733 // Close the polygon
1734 cornerList.push_back( cornerList[0] );
1735
1736 if( aPlotMode == SKETCH )
1737 PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
1738 else
1739 {
1740 #ifdef GBR_USE_MACROS_FOR_CHAMFERED_ROUND_RECT
1741 if( m_gerberDisableApertMacros )
1742 {
1743 PlotGerberRegion( cornerList, &gbr_metadata );
1744 }
1745 else
1746 {
1747 // An AM will be created. the shape must be in position 0,0 and orientation 0
1748 // to be able to reuse the same AM for pads having the same shape
1749 for( size_t ii = 0; ii < cornerList.size(); ii++ )
1750 {
1751 cornerList[ii] -= aShapePos;
1752 RotatePoint( &cornerList[ii], -aPadOrient );
1753 }
1754
1755 selectAperture( cornerList, aPadOrient/10.0,
1756 APERTURE::AM_FREE_POLYGON, gbr_metadata.GetApertureAttrib() );
1757 formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1758
1759 emitDcode( pos_dev, 3 );
1760 }
1761 #else
1762 PlotGerberRegion( cornerList, &gbr_metadata );
1763 #endif
1764 }
1765
1766 return;
1767 }
1768
1769 // Build the chamfered polygon (4 to 8 corners )
1770 TransformRoundChamferedRectToPolygon( outline, wxPoint( 0, 0 ), aPadSize, 0.0, 0,
1771 aChamferRatio, aChamferPositions, 0,
1772 GetPlotterArcHighDef(), ERROR_INSIDE );
1773
1774 // Build the corner list
1775 const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
1776
1777 // Generate the polygon (4 to 8 corners )
1778 for( int ii = 0; ii < corners.PointCount(); ii++ )
1779 cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
1780
1781 switch( cornerList.size() )
1782 {
1783 case 4:
1784 m_hasApertureOutline4P = true;
1785 selectAperture( cornerList, aPadOrient/10.0,
1786 APERTURE::APER_MACRO_OUTLINE4P, gbr_metadata.GetApertureAttrib() );
1787 break;
1788
1789 case 5:
1790 m_hasApertureChamferedRect = true;
1791 selectAperture( cornerList, aPadOrient/10.0,
1792 APERTURE::APER_MACRO_OUTLINE5P, gbr_metadata.GetApertureAttrib() );
1793 break;
1794
1795 case 6:
1796 m_hasApertureChamferedRect = true;
1797 selectAperture( cornerList, aPadOrient/10.0,
1798 APERTURE::APER_MACRO_OUTLINE6P, gbr_metadata.GetApertureAttrib() );
1799 break;
1800
1801 case 7:
1802 m_hasApertureChamferedRect = true;
1803 selectAperture( cornerList, aPadOrient/10.0,
1804 APERTURE::APER_MACRO_OUTLINE7P, gbr_metadata.GetApertureAttrib() );
1805 break;
1806
1807 case 8:
1808 m_hasApertureChamferedRect = true;
1809 selectAperture( cornerList, aPadOrient/10.0,
1810 APERTURE::APER_MACRO_OUTLINE8P, gbr_metadata.GetApertureAttrib() );
1811 break;
1812
1813 default:
1814 wxLogMessage( "FlashPadChamferRoundRect(): Unexpected number of corners (%d)",
1815 (int)cornerList.size() );
1816 break;
1817 }
1818
1819 formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1820
1821 emitDcode( pos_dev, 3 );
1822 }
1823
1824
FlashPadTrapez(const wxPoint & aPadPos,const wxPoint * aCorners,double aPadOrient,OUTLINE_MODE aTrace_Mode,void * aData)1825 void GERBER_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCorners,
1826 double aPadOrient, OUTLINE_MODE aTrace_Mode, void* aData )
1827
1828 {
1829 // polygon corners list
1830 std::vector<wxPoint> cornerList = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
1831
1832 // Draw the polygon and fill the interior as required
1833 for( unsigned ii = 0; ii < 4; ii++ )
1834 {
1835 RotatePoint( &cornerList[ii], aPadOrient );
1836 cornerList[ii] += aPadPos;
1837 }
1838
1839 // Close the polygon
1840 cornerList.push_back( cornerList[0] );
1841 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1842
1843 GBR_METADATA metadata;
1844
1845 if( gbr_metadata )
1846 metadata = *gbr_metadata;
1847
1848 if( aTrace_Mode == SKETCH )
1849 {
1850 PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), &metadata );
1851 return;
1852 }
1853
1854 // Plot a filled polygon:
1855 #ifdef GBR_USE_MACROS_FOR_TRAPEZOID
1856 if( !m_gerberDisableApertMacros )
1857 #endif
1858 {
1859 m_hasApertureOutline4P = true;
1860 DPOINT pos_dev = userToDeviceCoordinates( aPadPos );
1861 // polygon corners list
1862 std::vector<wxPoint> corners = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
1863 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1864 selectAperture( corners, aPadOrient/10.0, APERTURE::APER_MACRO_OUTLINE4P, aperture_attrib );
1865
1866 if( gbr_metadata )
1867 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1868
1869 emitDcode( pos_dev, 3 );
1870 return;
1871 }
1872
1873 PlotGerberRegion( cornerList, &metadata );
1874 }
1875
1876
FlashRegularPolygon(const wxPoint & aShapePos,int aDiameter,int aCornerCount,double aOrient,OUTLINE_MODE aTraceMode,void * aData)1877 void GERBER_PLOTTER::FlashRegularPolygon( const wxPoint& aShapePos, int aDiameter,
1878 int aCornerCount, double aOrient,
1879 OUTLINE_MODE aTraceMode, void* aData )
1880 {
1881 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1882
1883 GBR_METADATA metadata;
1884
1885 if( gbr_metadata )
1886 metadata = *gbr_metadata;
1887
1888 if( aTraceMode == SKETCH )
1889 {
1890 // Build the polygon:
1891 std::vector< wxPoint > cornerList;
1892
1893 double angle_delta = 3600.0 / aCornerCount; // in 0.1 degree
1894
1895 for( int ii = 0; ii < aCornerCount; ii++ )
1896 {
1897 double rot = aOrient + (angle_delta*ii);
1898 wxPoint vertice( aDiameter/2, 0 );
1899 RotatePoint( &vertice, rot );
1900 vertice += aShapePos;
1901 cornerList.push_back( vertice );
1902 }
1903
1904 cornerList.push_back( cornerList[0] ); // Close the shape
1905
1906 PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
1907 }
1908 else
1909 {
1910 DPOINT pos_dev = userToDeviceCoordinates( aShapePos );
1911
1912 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1913
1914 APERTURE::APERTURE_TYPE apert_type =
1915 (APERTURE::APERTURE_TYPE)(APERTURE::AT_REGULAR_POLY3 + aCornerCount - 3);
1916 selectAperture( aDiameter, aOrient, apert_type, aperture_attrib );
1917
1918 if( gbr_metadata )
1919 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1920
1921 emitDcode( pos_dev, 3 );
1922 }
1923 }
1924
1925
Text(const wxPoint & aPos,const COLOR4D & aColor,const wxString & aText,double aOrient,const wxSize & aSize,enum EDA_TEXT_HJUSTIFY_T aH_justify,enum EDA_TEXT_VJUSTIFY_T aV_justify,int aWidth,bool aItalic,bool aBold,bool aMultilineAllowed,void * aData)1926 void GERBER_PLOTTER::Text( const wxPoint& aPos, const COLOR4D& aColor,
1927 const wxString& aText, double aOrient, const wxSize& aSize,
1928 enum EDA_TEXT_HJUSTIFY_T aH_justify,
1929 enum EDA_TEXT_VJUSTIFY_T aV_justify, int aWidth, bool aItalic,
1930 bool aBold, bool aMultilineAllowed, void* aData )
1931 {
1932 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1933
1934 if( gbr_metadata )
1935 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1936
1937 PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth, aItalic,
1938 aBold, aMultilineAllowed, aData );
1939 }
1940
1941
SetLayerPolarity(bool aPositive)1942 void GERBER_PLOTTER::SetLayerPolarity( bool aPositive )
1943 {
1944 if( aPositive )
1945 fprintf( m_outputFile, "%%LPD*%%\n" );
1946 else
1947 fprintf( m_outputFile, "%%LPC*%%\n" );
1948 }
1949
1950
IsSamePoly(const std::vector<wxPoint> & aPolygon) const1951 bool APER_MACRO_FREEPOLY::IsSamePoly( const std::vector<wxPoint>& aPolygon ) const
1952 {
1953 return polyCompare( m_Corners, aPolygon );
1954 }
1955
1956
Format(FILE * aOutput,double aIu2GbrMacroUnit)1957 void APER_MACRO_FREEPOLY::Format( FILE * aOutput, double aIu2GbrMacroUnit )
1958 {
1959 // Write aperture header
1960 fprintf( aOutput, "%%AM%s%d*\n", AM_FREEPOLY_BASENAME, m_Id );
1961 fprintf( aOutput, "4,1,%d,", (int)m_Corners.size() );
1962
1963 // Insert a newline after curr_line_count_max coordinates.
1964 int curr_line_corner_count = 0;
1965 const int curr_line_count_max = 20; // <= 0 to disable newlines
1966
1967 for( size_t ii = 0; ii <= m_Corners.size(); ii++ )
1968 {
1969 int jj = ii;
1970
1971 if( ii >= m_Corners.size() )
1972 jj = 0;
1973
1974 // Note: parameter values are always mm or inches
1975 fprintf( aOutput, "%#f,%#f,",
1976 m_Corners[jj].x * aIu2GbrMacroUnit, -m_Corners[jj].y * aIu2GbrMacroUnit );
1977
1978 if( curr_line_count_max >= 0 && ++curr_line_corner_count >= curr_line_count_max )
1979 {
1980 fprintf( aOutput, "\n" );
1981 curr_line_corner_count = 0;
1982 }
1983 }
1984
1985 // output rotation parameter
1986 fputs( "$1*%\n", aOutput );
1987 }
1988
1989
Format(FILE * aOutput,double aIu2GbrMacroUnit)1990 void APER_MACRO_FREEPOLY_LIST::Format( FILE * aOutput, double aIu2GbrMacroUnit )
1991 {
1992 for( int idx = 0; idx < AmCount(); idx++ )
1993 m_AMList[idx].Format( aOutput, aIu2GbrMacroUnit );
1994 }
1995
1996
Append(const std::vector<wxPoint> & aPolygon)1997 void APER_MACRO_FREEPOLY_LIST::Append( const std::vector<wxPoint>& aPolygon )
1998 {
1999 m_AMList.emplace_back( aPolygon, AmCount() );
2000 }
2001
2002
FindAm(const std::vector<wxPoint> & aPolygon) const2003 int APER_MACRO_FREEPOLY_LIST::FindAm( const std::vector<wxPoint>& aPolygon ) const
2004 {
2005 for( int idx = 0; idx < AmCount(); idx++ )
2006 {
2007 if( m_AMList[idx].IsSamePoly( aPolygon ) )
2008 return idx;
2009 }
2010
2011 return -1;
2012 }
2013