KiCad PCB EDA Suite
Loading...
Searching...
No Matches
GERBER_plotter.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) 2022-2024 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
26#include <string_utils.h>
28#include <macros.h>
29#include <math/util.h> // for KiROUND
30#include <trigo.h>
31#include <wx/log.h>
32#include <cstdio>
33
34#include <build_version.h>
35
38
39#include <gbr_metadata.h>
40
41
42// if GBR_USE_MACROS is defined, pads having a shape that is not a Gerber primitive
43// will use a macro when possible
44// Old code will be removed only after many tests
45//
46// Note also: setting m_gerberDisableApertMacros to true disable all aperture macros
47// in Gerber files
48//
49#define GBR_USE_MACROS_FOR_CHAMFERED_ROUND_RECT
50#define GBR_USE_MACROS_FOR_CHAMFERED_RECT
51#define GBR_USE_MACROS_FOR_ROUNDRECT
52#define GBR_USE_MACROS_FOR_TRAPEZOID
53#define GBR_USE_MACROS_FOR_ROTATED_OVAL
54#define GBR_USE_MACROS_FOR_ROTATED_RECT
55#define GBR_USE_MACROS_FOR_CUSTOM_PAD
56
57// max count of corners to create a aperture macro for a custom shape.
58// provided just in case a aperture macro type free polygon creates issues
59// when the number of corners is too high.
60// (1 corner = up to 24 chars)
61// Gerber doc say max corners 5000. We use a slightly smaller value.
62// if a custom shape needs more than GBR_MACRO_FOR_CUSTOM_PAD_MAX_CORNER_COUNT, it
63// will be plot using a region.
64#define GBR_MACRO_FOR_CUSTOM_PAD_MAX_CORNER_COUNT 4990
65#define AM_FREEPOLY_BASENAME "FreePoly"
66
67
68// A helper function to compare 2 polygons: polygons are similar if they have the same
69// number of vertices and each vertex coordinate are similar, i.e. if the difference
70// between coordinates is small ( <= margin to accept rounding issues coming from polygon
71// geometric transforms like rotation
72static bool polyCompare( const std::vector<VECTOR2I>& aPolygon,
73 const std::vector<VECTOR2I>& aTestPolygon )
74{
75 // fast test: polygon sizes must be the same:
76 if( aTestPolygon.size() != aPolygon.size() )
77 return false;
78
79 const int margin = 2;
80
81 for( size_t jj = 0; jj < aPolygon.size(); jj++ )
82 {
83 if( std::abs( aPolygon[jj].x - aTestPolygon[jj].x ) > margin ||
84 std::abs( aPolygon[jj].y - aTestPolygon[jj].y ) > margin )
85 return false;
86 }
87
88 return true;
89}
90
91
93{
94 workFile = nullptr;
95 finalFile = nullptr;
98
99 // number of digits after the point (number of digits of the mantissa
100 // Be careful: the Gerber coordinates are stored in an integer
101 // so 6 digits (inches) or 5 digits (mm) is a good value
102 // To avoid overflow, 7 digits (inches) or 6 digits is a max.
103 // with lower values than 6 digits (inches) or 5 digits (mm),
104 // Creating self-intersecting polygons from non-intersecting polygons
105 // happen easily.
106 m_gerberUnitInch = false;
107 m_gerberUnitFmt = 6;
108 m_useX2format = true;
109 m_useNetAttributes = true;
111
112 m_hasApertureRoundRect = false; // true is at least one round rect aperture is in use
113 m_hasApertureRotOval = false; // true is at least one oval rotated aperture is in use
114 m_hasApertureRotRect = false; // true is at least one rect. rotated aperture is in use
115 m_hasApertureOutline4P = false; // true is at least one rotated rect or trapezoid pad
116 // aperture is in use
117 m_hasApertureChamferedRect = false; // true is at least one chamfered rect
118 // (no rounded corner) is in use
119}
120
121
122void GERBER_PLOTTER::SetViewport( const VECTOR2I& aOffset, double aIusPerDecimil,
123 double aScale, bool aMirror )
124{
125 wxASSERT( aMirror == false );
126 m_plotMirror = false;
127 m_plotOffset = aOffset;
128 wxASSERT( aScale == 1 ); // aScale parameter is not used in Gerber
129 m_plotScale = 1; // Plot scale is *always* 1.0
130
131 m_IUsPerDecimil = aIusPerDecimil;
132
133 // gives now a default value to iuPerDeviceUnit (because the units of the caller is now known)
134 // which could be modified later by calling SetGerberCoordinatesFormat()
135 m_iuPerDeviceUnit = pow( 10.0, m_gerberUnitFmt ) / ( m_IUsPerDecimil * 10000.0 );
136
137 // We don't handle the filmbox, and it's more useful to keep the
138 // origin at the origin
139 m_paperSize.x = 0;
140 m_paperSize.y = 0;
141}
142
143
144void GERBER_PLOTTER::SetGerberCoordinatesFormat( int aResolution, bool aUseInches )
145{
146 m_gerberUnitInch = aUseInches;
147 m_gerberUnitFmt = aResolution;
148
149 m_iuPerDeviceUnit = pow( 10.0, m_gerberUnitFmt ) / ( m_IUsPerDecimil * 10000.0 );
150
151 if( ! m_gerberUnitInch )
152 m_iuPerDeviceUnit *= 25.4; // gerber output in mm
153}
154
155
156void GERBER_PLOTTER::emitDcode( const VECTOR2D& pt, int dcode )
157{
158 fprintf( m_outputFile, "X%dY%dD%02d*\n", KiROUND( pt.x ), KiROUND( pt.y ), dcode );
159}
160
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
170}
171
172
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
186}
187
188
189void 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
196void GERBER_PLOTTER::EndBlock( void* aData )
197{
198 // Remove all net attributes from object attributes dictionary
200}
201
202
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 )
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
237bool GERBER_PLOTTER::StartPlot( const wxString& aPageNumber )
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
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" ) );
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
310 pen_min ) );
311 }
312
313 return true;
314}
315
316
318{
319 char line[1024];
320
321 wxASSERT( m_outputFile );
322
323 /* Outfile is actually a temporary file i.e. workFile */
324 fputs( "M02*\n", m_outputFile );
325 fflush( m_outputFile );
326
327 fclose( workFile );
328 workFile = wxFopen( m_workFilename, wxT( "rt" ));
329 wxASSERT( workFile );
331
332 // Placement of apertures in RS274X
333 while( fgets( line, 1024, workFile ) )
334 {
335 fputs( line, m_outputFile );
336
337 char* substr = strtok( line, "\n\r" );
338
339 if( substr && strcmp( substr, "G04 APERTURE LIST*" ) == 0 )
340 {
341 // Add aperture list macro:
345 {
346 fputs( "G04 Aperture macros list*\n", m_outputFile );
347
350
353
356
359
361 {
366 }
367
369 {
370 // aperture sizes are in inch or mm, regardless the
371 // coordinates format
372 double fscale = 0.0001 * m_plotScale / m_IUsPerDecimil; // inches
373
374 if(! m_gerberUnitInch )
375 fscale *= 25.4; // size in mm
376
378 }
379
380 fputs( "G04 Aperture macros list end*\n", m_outputFile );
381 }
382
384 fputs( "G04 APERTURE END LIST*\n", m_outputFile );
385 }
386 }
387
388 fclose( workFile );
389 fclose( finalFile );
390 ::wxRemoveFile( m_workFilename );
391 m_outputFile = nullptr;
392
393 return true;
394}
395
396
397void GERBER_PLOTTER::SetCurrentLineWidth( int aWidth, void* aData )
398{
399 if( aWidth == DO_NOT_SET_LINE_WIDTH )
400 return;
401 else if( aWidth == USE_DEFAULT_LINE_WIDTH )
403
404 wxASSERT_MSG( aWidth >= 0, "Plotter called to set negative pen width" );
405
406 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
407 int aperture_attribute = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
408
410 aperture_attribute );
411 m_currentPenWidth = aWidth;
412}
413
414
415int GERBER_PLOTTER::GetOrCreateAperture( const VECTOR2I& aSize, int aRadius,
416 const EDA_ANGLE& aRotation, APERTURE::APERTURE_TYPE aType,
417 int aApertureAttribute )
418{
419 int last_D_code = 9;
420
421 // Search an existing aperture
422 for( int idx = 0; idx < (int)m_apertures.size(); ++idx )
423 {
424 APERTURE* tool = &m_apertures[idx];
425 last_D_code = tool->m_DCode;
426
427 if( (tool->m_Type == aType) && (tool->m_Size == aSize) &&
428 (tool->m_Radius == aRadius) && (tool->m_Rotation == aRotation) &&
429 (tool->m_ApertureAttribute == aApertureAttribute) )
430 return idx;
431 }
432
433 // Allocate a new aperture
434 APERTURE new_tool;
435 new_tool.m_Size = aSize;
436 new_tool.m_Type = aType;
437 new_tool.m_Radius = aRadius;
438 new_tool.m_Rotation = aRotation;
439 new_tool.m_DCode = last_D_code + 1;
440 new_tool.m_ApertureAttribute = aApertureAttribute;
441
442 m_apertures.push_back( new_tool );
443
444 return m_apertures.size() - 1;
445}
446
447
448int GERBER_PLOTTER::GetOrCreateAperture( const std::vector<VECTOR2I>& aCorners,
449 const EDA_ANGLE& aRotation, APERTURE::APERTURE_TYPE aType,
450 int aApertureAttribute )
451{
452 int last_D_code = 9;
453
454 // For APERTURE::AM_FREE_POLYGON aperture macros, we need to create the macro
455 // on the fly, because due to the fact the vertex count is not a constant we
456 // cannot create a static definition.
457 if( APERTURE::AM_FREE_POLYGON == aType )
458 {
459 int idx = m_am_freepoly_list.FindAm( aCorners );
460
461 if( idx < 0 )
462 m_am_freepoly_list.Append( aCorners );
463 }
464
465 // Search an existing aperture
466 for( int idx = 0; idx < (int)m_apertures.size(); ++idx )
467 {
468 APERTURE* tool = &m_apertures[idx];
469
470 last_D_code = tool->m_DCode;
471
472 if( (tool->m_Type == aType) &&
473 (tool->m_Corners.size() == aCorners.size() ) &&
474 (tool->m_Rotation == aRotation) &&
475 (tool->m_ApertureAttribute == aApertureAttribute) )
476 {
477 // A candidate is found. the corner lists must be similar
478 bool is_same = polyCompare( tool->m_Corners, aCorners );
479
480 if( is_same )
481 return idx;
482 }
483 }
484
485 // Allocate a new aperture
486 APERTURE new_tool;
487
488 new_tool.m_Corners = aCorners;
489 new_tool.m_Size = VECTOR2I( 0, 0 ); // Not used
490 new_tool.m_Type = aType;
491 new_tool.m_Radius = 0; // Not used
492 new_tool.m_Rotation = aRotation;
493 new_tool.m_DCode = last_D_code + 1;
494 new_tool.m_ApertureAttribute = aApertureAttribute;
495
496 m_apertures.push_back( new_tool );
497
498 return m_apertures.size() - 1;
499}
500
501
502void GERBER_PLOTTER::selectAperture( const VECTOR2I& aSize, int aRadius, const EDA_ANGLE& aRotation,
503 APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
504{
505 bool change = ( m_currentApertureIdx < 0 ) ||
506 ( m_apertures[m_currentApertureIdx].m_Type != aType ) ||
507 ( m_apertures[m_currentApertureIdx].m_Size != aSize ) ||
508 ( m_apertures[m_currentApertureIdx].m_Radius != aRadius ) ||
509 ( m_apertures[m_currentApertureIdx].m_Rotation != aRotation );
510
511 if( !change )
512 change = m_apertures[m_currentApertureIdx].m_ApertureAttribute != aApertureAttribute;
513
514 if( change )
515 {
516 // Pick an existing aperture or create a new one
517 m_currentApertureIdx = GetOrCreateAperture( aSize, aRadius, aRotation, aType,
518 aApertureAttribute );
519 fprintf( m_outputFile, "D%d*\n", m_apertures[m_currentApertureIdx].m_DCode );
520 }
521}
522
523
524void GERBER_PLOTTER::selectAperture( const std::vector<VECTOR2I>& aCorners,
525 const EDA_ANGLE& aRotation, APERTURE::APERTURE_TYPE aType,
526 int aApertureAttribute )
527{
528 bool change = ( m_currentApertureIdx < 0 ) ||
529 ( m_apertures[m_currentApertureIdx].m_Type != aType ) ||
530 ( m_apertures[m_currentApertureIdx].m_Corners.size() != aCorners.size() ) ||
531 ( m_apertures[m_currentApertureIdx].m_Rotation != aRotation );
532
533 if( !change ) // Compare corner lists
534 {
535 for( size_t ii = 0; ii < aCorners.size(); ii++ )
536 {
537 if( aCorners[ii] != m_apertures[m_currentApertureIdx].m_Corners[ii] )
538 {
539 change = true;
540 break;
541 }
542 }
543 }
544
545 if( !change )
546 change = m_apertures[m_currentApertureIdx].m_ApertureAttribute != aApertureAttribute;
547
548 if( change )
549 {
550 // Pick an existing aperture or create a new one
551 m_currentApertureIdx = GetOrCreateAperture( aCorners, aRotation, aType,
552 aApertureAttribute );
553 fprintf( m_outputFile, "D%d*\n", m_apertures[m_currentApertureIdx].m_DCode );
554 }
555}
556
557
558void GERBER_PLOTTER::selectAperture( int aDiameter, const EDA_ANGLE& aPolygonRotation,
559 APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
560{
561 // Pick an existing aperture or create a new one, matching the
562 // aDiameter, aPolygonRotation, type and attributes for type =
563 // AT_REGULAR_POLY3 to AT_REGULAR_POLY12
564
567
568 VECTOR2I size( aDiameter, (int) ( aPolygonRotation.AsDegrees() * 1000.0 ) );
569 selectAperture( VECTOR2I( 0, 0 ), aDiameter / 2, aPolygonRotation, aType, aApertureAttribute );
570}
571
573{
574 wxASSERT( m_outputFile );
575
576 bool useX1StructuredComment = false;
577
578 if( !m_useX2format )
579 useX1StructuredComment = true;
580
581 // Init
582 for( APERTURE& tool : m_apertures )
583 {
584 // aperture sizes are in inch or mm, regardless the
585 // coordinates format
586 double fscale = 0.0001 * m_plotScale / m_IUsPerDecimil; // inches
587
588 if(! m_gerberUnitInch )
589 fscale *= 25.4; // size in mm
590
591 int attribute = tool.m_ApertureAttribute;
592
593 if( attribute != m_apertureAttribute )
594 {
597 useX1StructuredComment ).c_str(), m_outputFile );
598 }
599
600 fprintf( m_outputFile, "%%ADD%d", tool.m_DCode );
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 {
612 fprintf( m_outputFile, "C,%#f*%%\n", tool.GetDiameter() * fscale );
613 break;
614
616 fprintf( m_outputFile, "R,%#fX%#f*%%\n", tool.m_Size.x * fscale,
617 tool.m_Size.y * fscale );
618 break;
619
621 fprintf( m_outputFile, "C,%#f*%%\n", tool.m_Size.x * fscale );
622 break;
623
625 fprintf( m_outputFile, "O,%#fX%#f*%%\n", tool.m_Size.x * fscale,
626 tool.m_Size.y * fscale );
627 break;
628
640 fprintf( m_outputFile, "P,%#fX%dX%#f*%%\n", tool.GetDiameter() * fscale,
641 tool.GetRegPolyVerticeCount(), tool.GetRotation().AsDegrees() );
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 VECTOR2I half_size( tool.m_Size.x/2-tool.m_Radius, tool.m_Size.y/2-tool.m_Radius );
649
650 // Ensure half_size.x and half_size.y > minimal value to avoid shapes
651 // with null size (especially the rectangle with coordinates corners)
652 // Because the minimal value for a non nul Gerber coord in 10nm
653 // in format 4.5, use 10 nm as minimal value.
654 // (Even in 4.6 format, use 10 nm, because gerber viewers can have
655 // a internal unit bigger than 1 nm)
656 const int min_size_value = 10;
657 half_size.x = std::max( half_size.x, min_size_value );
658 half_size.y = std::max( half_size.y, min_size_value );
659
660 corners.emplace_back( -half_size.x, -half_size.y );
661 corners.emplace_back( half_size.x, -half_size.y );
662 corners.emplace_back( half_size.x, half_size.y );
663 corners.emplace_back( -half_size.x, half_size.y );
664
665 // Rotate the corner coordinates:
666 for( int ii = 0; ii < 4; ii++ )
667 RotatePoint( corners[ii], -tool.m_Rotation );
668
669 fprintf( m_outputFile, "%s,%#fX", APER_MACRO_ROUNDRECT_NAME, tool.m_Radius * fscale );
670
671 // Add each corner
672 for( int ii = 0; ii < 4; ii++ )
673 {
674 fprintf( m_outputFile, "%#fX%#fX", corners[ii].x * fscale, corners[ii].y * fscale );
675 }
676
677 fprintf( m_outputFile, "0*%%\n" );
678 }
679 break;
680
681 case APERTURE::AM_ROT_RECT: // Aperture macro for rotated rect pads
682 fprintf( m_outputFile, "%s,%#fX%#fX%#f*%%\n", APER_MACRO_ROT_RECT_NAME,
683 tool.m_Size.x * fscale, tool.m_Size.y * fscale, tool.m_Rotation.AsDegrees() );
684 break;
685
686 case APERTURE::APER_MACRO_OUTLINE4P: // Aperture macro for trapezoid pads
687 case APERTURE::APER_MACRO_OUTLINE5P: // Aperture macro for chamfered rect pads
688 case APERTURE::APER_MACRO_OUTLINE6P: // Aperture macro for chamfered rect pads
689 case APERTURE::APER_MACRO_OUTLINE7P: // Aperture macro for chamfered rect pads
690 case APERTURE::APER_MACRO_OUTLINE8P: // Aperture macro for chamfered rect pads
691 switch( tool.m_Type )
692 {
694 fprintf( m_outputFile, "%s,", APER_MACRO_OUTLINE4P_NAME );
695 break;
697 fprintf( m_outputFile, "%s,", APER_MACRO_OUTLINE5P_NAME );
698 break;
700 fprintf( m_outputFile, "%s,", APER_MACRO_OUTLINE6P_NAME );
701 break;
703 fprintf( m_outputFile, "%s,", APER_MACRO_OUTLINE7P_NAME );
704 break;
706 fprintf( m_outputFile, "%s,", APER_MACRO_OUTLINE8P_NAME );
707 break;
708 default:
709 break;
710 }
711
712
713 // Output all corners (should be 4 to 8 corners)
714 // Remember: the Y coordinate must be negated, due to the fact in Pcbnew
715 // the Y axis is from top to bottom
716 for( size_t ii = 0; ii < tool.m_Corners.size(); ii++ )
717 {
718 fprintf( m_outputFile, "%#fX%#fX", tool.m_Corners[ii].x * fscale,
719 -tool.m_Corners[ii].y * fscale );
720 }
721
722 // close outline and output rotation
723 fprintf( m_outputFile, "%#f*%%\n", tool.m_Rotation.AsDegrees() );
724 break;
725
726 case APERTURE::AM_ROTATED_OVAL: // Aperture macro for rotated oval pads
727 // (not rotated is a primitive)
728 // m_Size.x = full length; m_Size.y = width, and the macro aperture expects
729 // the position of ends
730 {
731 // the seg_len is the distance between the 2 circle centers
732 int seg_len = tool.m_Size.x - tool.m_Size.y;
733 // Center of the circle on the segment start point:
734 VECTOR2I start( seg_len/2, 0 );
735 // Center of the circle on the segment end point:
736 VECTOR2I end( - seg_len/2, 0 );
737
738 RotatePoint( start, tool.m_Rotation );
739 RotatePoint( end, tool.m_Rotation );
740
741 fprintf( m_outputFile, "%s,%#fX%#fX%#fX%#fX%#fX0*%%\n", APER_MACRO_SHAPE_OVAL_NAME,
742 tool.m_Size.y * fscale, // width
743 start.x * fscale, -start.y * fscale, // X,Y corner start pos
744 end.x * fscale, -end.y * fscale ); // X,Y cornerend pos
745 }
746 break;
747
749 {
750 // Find the aperture macro name in the list of aperture macro
751 // created on the fly for this polygon:
752 int idx = m_am_freepoly_list.FindAm( tool.m_Corners );
753
754 // Write DCODE id ( "%ADDxx" is already in buffer) and rotation
755 // the full line is something like :%ADD12FreePoly1,45.000000*%
756 fprintf( m_outputFile, "%s%d,%#f*%%\n", AM_FREEPOLY_BASENAME, idx,
757 tool.m_Rotation.AsDegrees() );
758 break;
759 }
760 }
761
762 m_apertureAttribute = attribute;
763
764 // Currently reset the aperture attribute. Perhaps a better optimization
765 // is to store the last attribute
766 if( attribute )
767 {
768 if( m_useX2format )
769 fputs( "%TD*%\n", m_outputFile );
770 else
771 fputs( "G04 #@! TD*\n", m_outputFile );
772
774 }
775
776 }
777}
778
779
780void GERBER_PLOTTER::PenTo( const VECTOR2I& aPos, char plume )
781{
782 wxASSERT( m_outputFile );
783 VECTOR2D pos_dev = userToDeviceCoordinates( aPos );
784
785 switch( plume )
786 {
787 case 'Z':
788 break;
789
790 case 'U':
791 emitDcode( pos_dev, 2 );
792 break;
793
794 case 'D':
795 emitDcode( pos_dev, 1 );
796 }
797
798 m_penState = plume;
799}
800
801
802void GERBER_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width )
803{
804 std::vector<VECTOR2I> cornerList;
805
806 cornerList.reserve( 5 );
807
808 // Build corners list
809 cornerList.push_back( p1 );
810
811 VECTOR2I corner( p1.x, p2.y );
812 cornerList.push_back( corner );
813 cornerList.push_back( p2 );
814 corner.x = p2.x;
815 corner.y = p1.y;
816 cornerList.push_back( corner );
817 cornerList.push_back( p1 );
818
819 PlotPoly( cornerList, fill, width );
820}
821
822
823void GERBER_PLOTTER::Circle( const VECTOR2I& aCenter, int aDiameter, FILL_T aFill, int aWidth )
824{
825 Arc( aCenter, ANGLE_0, ANGLE_180, aDiameter / 2, aFill, aWidth );
826 Arc( aCenter, ANGLE_180, ANGLE_180, aDiameter / 2, aFill, aWidth );
827}
828
829
830
831void GERBER_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
832 const EDA_ANGLE& aAngle, double aRadius, FILL_T aFill, int aWidth )
833{
834 SetCurrentLineWidth( aWidth );
835
836 double arcLength = std::abs( aRadius * aAngle.AsRadians() );
837
838 if( arcLength < 100 || std::abs( aAngle.AsDegrees() ) < 0.1 )
839 {
840 // Prevent plotting very short arcs as full circles, especially with 4.5 mm precision.
841 // Also reduce the risk of integer overflow issues.
842 polyArc( aCenter, aStartAngle, aAngle, aRadius, aFill, aWidth );
843 }
844 else
845 {
846 EDA_ANGLE endAngle = aStartAngle + aAngle;
847
848 // aFill is not used here.
849 plotArc( aCenter, aStartAngle, endAngle, aRadius, false );
850 }
851}
852
853
854void GERBER_PLOTTER::plotArc( const SHAPE_ARC& aArc, bool aPlotInRegion )
855{
856 VECTOR2I start( aArc.GetP0() );
857 VECTOR2I end( aArc.GetP1() );
858 VECTOR2I center( aArc.GetCenter() );
859
860 if( !aPlotInRegion )
861 MoveTo( start);
862 else
863 LineTo( start );
864
865 VECTOR2D devEnd = userToDeviceCoordinates( end );
866
867 // devRelCenter is the position on arc center relative to the arc start, in Gerber coord.
868 // Warning: it is **not** userToDeviceCoordinates( center - start ) when the plotter
869 // has an offset.
870 VECTOR2D devRelCenter = userToDeviceCoordinates( center ) - userToDeviceCoordinates( start );
871
872 // We need to know if the arc is CW or CCW in device coordinates, so build this arc.
873 SHAPE_ARC deviceArc( userToDeviceCoordinates( start ),
875 devEnd, 0 );
876
877 fprintf( m_outputFile, "G75*\n" ); // Multiquadrant (360 degrees) mode
878
879 if( deviceArc.IsClockwise() )
880 fprintf( m_outputFile, "G02*\n" ); // Active circular interpolation, CW
881 else
882 fprintf( m_outputFile, "G03*\n" ); // Active circular interpolation, CCW
883
884 fprintf( m_outputFile, "X%dY%dI%dJ%dD01*\n",
885 KiROUND( devEnd.x ), KiROUND( devEnd.y ),
886 KiROUND( devRelCenter.x ), KiROUND( devRelCenter.y ) );
887
888 fprintf( m_outputFile, "G01*\n" ); // Back to linear interpolate (perhaps useless here).
889}
890
891
892void GERBER_PLOTTER::plotArc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle,
893 const EDA_ANGLE& aEndAngle, double aRadius, bool aPlotInRegion )
894{
895 VECTOR2I start, end;
896 start.x = KiROUND( aCenter.x + aRadius * aStartAngle.Cos() );
897 start.y = KiROUND( aCenter.y + aRadius * aStartAngle.Sin() );
898
899 if( !aPlotInRegion )
900 MoveTo( start );
901 else
902 LineTo( start );
903
904 end.x = KiROUND( aCenter.x + aRadius * aEndAngle.Cos() );
905 end.y = KiROUND( aCenter.y + aRadius * aEndAngle.Sin() );
906 VECTOR2D devEnd = userToDeviceCoordinates( end );
907 // devRelCenter is the position on arc center relative to the arc start, in Gerber coord.
908 VECTOR2D devRelCenter = userToDeviceCoordinates( aCenter ) - userToDeviceCoordinates( start );
909
910 fprintf( m_outputFile, "G75*\n" ); // Multiquadrant (360 degrees) mode
911
912 if( aStartAngle > aEndAngle )
913 fprintf( m_outputFile, "G03*\n" ); // Active circular interpolation, CCW
914 else
915 fprintf( m_outputFile, "G02*\n" ); // Active circular interpolation, CW
916
917 fprintf( m_outputFile, "X%dY%dI%dJ%dD01*\n",
918 KiROUND( devEnd.x ), KiROUND( devEnd.y ),
919 KiROUND( devRelCenter.x ), KiROUND( devRelCenter.y ) );
920
921 fprintf( m_outputFile, "G01*\n" ); // Back to linear interpolate (perhaps useless here).
922}
923
924
926{
927 if( aPoly.PointCount() <= 2 )
928 return;
929
930 bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
931
932 if( aGbrMetadata )
933 {
934 std::string attrib = aGbrMetadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
935
936 if( !attrib.empty() )
937 {
938 fputs( attrib.c_str(), m_outputFile );
939 clearTA_AperFunction = true;
940 }
941 }
942
943 PlotPoly( aPoly, FILL_T::FILLED_SHAPE, 0 , aGbrMetadata );
944
945 // Clear the TA attribute, to avoid the next item to inherit it:
946 if( clearTA_AperFunction )
947 {
948 if( m_useX2format )
949 {
950 fputs( "%TD.AperFunction*%\n", m_outputFile );
951 }
952 else
953 {
954 fputs( "G04 #@! TD.AperFunction*\n", m_outputFile );
955 }
956 }
957}
958
959
960void GERBER_PLOTTER::PlotGerberRegion( const std::vector<VECTOR2I>& aCornerList,
961 GBR_METADATA* aGbrMetadata )
962{
963 if( aCornerList.size() <= 2 )
964 return;
965
966 bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
967
968 if( aGbrMetadata )
969 {
970 std::string attrib = aGbrMetadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
971
972 if( !attrib.empty() )
973 {
974 fputs( attrib.c_str(), m_outputFile );
975 clearTA_AperFunction = true;
976 }
977 }
978
979 PlotPoly( aCornerList, FILL_T::FILLED_SHAPE, 0, aGbrMetadata );
980
981 // Clear the TA attribute, to avoid the next item to inherit it:
982 if( clearTA_AperFunction )
983 {
984 if( m_useX2format )
985 {
986 fputs( "%TD.AperFunction*%\n", m_outputFile );
987 }
988 else
989 {
990 fputs( "G04 #@! TD.AperFunction*\n", m_outputFile );
991 }
992 }
993}
994
995
997 int aWidth, GBR_METADATA* aGbrMetadata )
998{
999 // plot a filled polygon using Gerber region, therefore adding X2 attributes
1000 // to the solid polygon
1001 if( aWidth || aFill == FILL_T::NO_FILL )
1002 PlotPoly( aPoly, FILL_T::NO_FILL, aWidth, aGbrMetadata );
1003
1004 if( aFill != FILL_T::NO_FILL )
1005 PlotGerberRegion( aPoly, aGbrMetadata );
1006}
1007
1008
1009void GERBER_PLOTTER::PlotPoly( const SHAPE_LINE_CHAIN& aPoly, FILL_T aFill, int aWidth,
1010 void* aData )
1011{
1012 if( aPoly.CPoints().size() <= 1 )
1013 return;
1014
1015 // Gerber format does not know filled polygons with thick outline
1016 // Therefore, to plot a filled polygon with outline having a thickness,
1017 // one should plot outline as thick segments
1018 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1019
1020 if( gbr_metadata )
1021 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1022
1023 if( aFill != FILL_T::NO_FILL )
1024 {
1025 fputs( "G36*\n", m_outputFile );
1026
1027 MoveTo( VECTOR2I( aPoly.CPoint( 0 ) ) );
1028
1029 fputs( "G01*\n", m_outputFile ); // Set linear interpolation.
1030
1031 for( int ii = 1; ii < aPoly.PointCount(); ii++ )
1032 {
1033 int arcindex = aPoly.ArcIndex( ii );
1034
1035 if( arcindex < 0 )
1036 {
1038 LineTo( VECTOR2I( aPoly.CPoint( ii ) ) );
1039 }
1040 else
1041 {
1042 const SHAPE_ARC& arc = aPoly.Arc( arcindex );
1043
1044 plotArc( arc, true );
1045
1046 // skip points on arcs, since we plot the arc itself
1047 while( ii+1 < aPoly.PointCount() && arcindex == aPoly.ArcIndex( ii+1 ) )
1048 ii++;
1049 }
1050 }
1051
1052 // If the polygon is not closed, close it:
1053 if( aPoly.CPoint( 0 ) != aPoly.CPoint( -1 ) )
1054 FinishTo( VECTOR2I( aPoly.CPoint( 0 ) ) );
1055
1056 fputs( "G37*\n", m_outputFile );
1057 }
1058
1059 if( aWidth > 0 || aFill == FILL_T::NO_FILL ) // Draw the polyline/polygon outline
1060 {
1061 SetCurrentLineWidth( aWidth, gbr_metadata );
1062
1063 MoveTo( VECTOR2I( aPoly.CPoint( 0 ) ) );
1064
1065 for( int ii = 1; ii < aPoly.PointCount(); ii++ )
1066 {
1067 int arcindex = aPoly.ArcIndex( ii );
1068
1069 if( arcindex < 0 )
1070 {
1072 LineTo( VECTOR2I( aPoly.CPoint( ii ) ) );
1073 }
1074 else
1075 {
1076 const SHAPE_ARC& arc = aPoly.Arc( arcindex );
1077
1078 plotArc( arc, true );
1079
1080 // skip points on arcs, since we plot the arc itself
1081 while( ii+1 < aPoly.PointCount() && arcindex == aPoly.ArcIndex( ii+1 ) )
1082 ii++;
1083 }
1084 }
1085
1086 // Ensure the thick outline is closed for filled polygons
1087 // (if not filled, could be only a polyline)
1088 if( ( aPoly.CPoint( 0 ) != aPoly.CPoint( -1 ) )
1089 && ( aPoly.IsClosed() || aFill != FILL_T::NO_FILL ) )
1090 LineTo( VECTOR2I( aPoly.CPoint( 0 ) ) );
1091
1092 PenFinish();
1093 }
1094}
1095
1096void GERBER_PLOTTER::PlotPoly( const std::vector<VECTOR2I>& aCornerList, FILL_T aFill, int aWidth,
1097 void * aData )
1098{
1099 if( aCornerList.size() <= 1 )
1100 return;
1101
1102 // Gerber format does not know filled polygons with thick outline
1103 // Therefore, to plot a filled polygon with outline having a thickness,
1104 // one should plot outline as thick segments
1105 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1106
1107 if( gbr_metadata )
1108 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1109
1110 if( aFill != FILL_T::NO_FILL )
1111 {
1112 fputs( "G36*\n", m_outputFile );
1113
1114 MoveTo( aCornerList[0] );
1115 fputs( "G01*\n", m_outputFile ); // Set linear interpolation.
1116
1117 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1118 LineTo( aCornerList[ii] );
1119
1120 // If the polygon is not closed, close it:
1121 if( aCornerList[0] != aCornerList[aCornerList.size()-1] )
1122 FinishTo( aCornerList[0] );
1123
1124 fputs( "G37*\n", m_outputFile );
1125 }
1126
1127 if( aWidth > 0 || aFill == FILL_T::NO_FILL ) // Draw the polyline/polygon outline
1128 {
1129 SetCurrentLineWidth( aWidth, gbr_metadata );
1130
1131 MoveTo( aCornerList[0] );
1132
1133 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1134 LineTo( aCornerList[ii] );
1135
1136 // Ensure the thick outline is closed for filled polygons
1137 // (if not filled, could be only a polyline)
1138 if( aFill != FILL_T::NO_FILL && ( aCornerList[aCornerList.size() - 1] != aCornerList[0] ) )
1139 LineTo( aCornerList[0] );
1140
1141 PenFinish();
1142 }
1143}
1144
1145
1146void GERBER_PLOTTER::ThickSegment( const VECTOR2I& start, const VECTOR2I& end, int width,
1147 OUTLINE_MODE tracemode, void* aData )
1148{
1149 if( tracemode == FILLED )
1150 {
1151 GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1152 SetCurrentLineWidth( width, gbr_metadata );
1153
1154 if( gbr_metadata )
1155 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1156
1157 MoveTo( start );
1158 FinishTo( end );
1159 }
1160 else
1161 {
1163 segmentAsOval( start, end, width, tracemode );
1164 }
1165}
1166
1167
1168void GERBER_PLOTTER::ThickArc( const VECTOR2D& aCentre, const EDA_ANGLE& aStartAngle,
1169 const EDA_ANGLE& aAngle, double aRadius, int aWidth,
1170 OUTLINE_MODE aTraceMode, void* aData )
1171{
1172 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1173 SetCurrentLineWidth( aWidth, gbr_metadata );
1174
1175 if( gbr_metadata )
1176 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1177
1178 if( aTraceMode == FILLED )
1179 {
1180 Arc( aCentre, aStartAngle, aAngle, aRadius, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1181 }
1182 else
1183 {
1185 Arc( aCentre, aStartAngle, aAngle, aRadius - ( aWidth - m_currentPenWidth ) / 2,
1186 FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1187 Arc( aCentre, aStartAngle, aAngle, aRadius + ( aWidth - m_currentPenWidth ) / 2,
1188 FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1189 }
1190}
1191
1192
1193void GERBER_PLOTTER::ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width,
1194 OUTLINE_MODE tracemode, void* aData )
1195{
1196 GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1197 SetCurrentLineWidth( width, gbr_metadata );
1198
1199 if( gbr_metadata )
1200 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1201
1202 if( tracemode == FILLED )
1203 {
1204 Rect( p1, p2, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1205 }
1206 else
1207 {
1209 VECTOR2I offsetp1( p1.x - ( width - m_currentPenWidth ) / 2,
1210 p1.y - (width - m_currentPenWidth) / 2 );
1211 VECTOR2I offsetp2( p2.x + ( width - m_currentPenWidth ) / 2,
1212 p2.y + (width - m_currentPenWidth) / 2 );
1213 Rect( offsetp1, offsetp2, FILL_T::NO_FILL, -1 );
1214 offsetp1.x += (width - m_currentPenWidth);
1215 offsetp1.y += (width - m_currentPenWidth);
1216 offsetp2.x -= (width - m_currentPenWidth);
1217 offsetp2.y -= (width - m_currentPenWidth);
1218 Rect( offsetp1, offsetp2, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1219 }
1220}
1221
1222
1223void GERBER_PLOTTER::ThickCircle( const VECTOR2I& pos, int diametre, int width,
1224 OUTLINE_MODE tracemode, void* aData )
1225{
1226 GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1227 SetCurrentLineWidth( width, gbr_metadata );
1228
1229 if( gbr_metadata )
1230 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1231
1232 if( tracemode == FILLED )
1233 {
1234 Circle( pos, diametre, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1235 }
1236 else
1237 {
1239 Circle( pos, diametre - (width - m_currentPenWidth), FILL_T::NO_FILL,
1241 Circle( pos, diametre + (width - m_currentPenWidth), FILL_T::NO_FILL,
1243 }
1244}
1245
1246
1247void GERBER_PLOTTER::FilledCircle( const VECTOR2I& pos, int diametre,
1248 OUTLINE_MODE tracemode, void* aData )
1249{
1250 // A filled circle is a graphic item, not a pad.
1251 // So it is drawn, not flashed.
1252 GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1253
1254 if( gbr_metadata )
1255 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1256
1257 if( tracemode == FILLED )
1258 {
1259 // Draw a circle of diameter = diameter/2 with a line thickness = radius,
1260 // To create a filled circle
1261 SetCurrentLineWidth( diametre/2, gbr_metadata );
1262 Circle( pos, diametre/2, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1263 }
1264 else
1265 {
1267 Circle( pos, diametre, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1268 }
1269}
1270
1271
1272void GERBER_PLOTTER::FlashPadCircle( const VECTOR2I& pos, int diametre, OUTLINE_MODE trace_mode,
1273 void* aData )
1274{
1275 VECTOR2I size( diametre, diametre );
1276 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1277
1278 if( trace_mode == SKETCH )
1279 {
1280 if( gbr_metadata )
1281 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1282
1284
1285 Circle( pos, diametre, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1286 }
1287 else
1288 {
1289 VECTOR2D pos_dev = userToDeviceCoordinates( pos );
1290
1291 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1292 selectAperture( size, 0, ANGLE_0, APERTURE::AT_CIRCLE, aperture_attrib );
1293
1294 if( gbr_metadata )
1295 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1296
1297 emitDcode( pos_dev, 3 );
1298 }
1299}
1300
1301
1302void GERBER_PLOTTER::FlashPadOval( const VECTOR2I& aPos, const VECTOR2I& aSize,
1303 const EDA_ANGLE& aOrient, OUTLINE_MODE aTraceMode, void* aData )
1304{
1305 wxASSERT( m_outputFile );
1306
1307 VECTOR2I size( aSize );
1308 EDA_ANGLE orient( aOrient );
1309 orient.Normalize();
1310 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1311
1312 // Flash a vertical or horizontal shape (this is a basic aperture).
1313 if( orient.IsCardinal() && aTraceMode == FILLED )
1314 {
1315 if( orient.IsCardinal90() )
1316 std::swap( size.x, size.y );
1317
1318 VECTOR2D pos_device = userToDeviceCoordinates( aPos );
1319 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1320 selectAperture( size, 0, ANGLE_0, APERTURE::AT_OVAL, aperture_attrib );
1321
1322 if( gbr_metadata )
1323 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1324
1325 emitDcode( pos_device, 3 );
1326 }
1327 else // Plot pad as region.
1328 // Only regions and flashed items accept a object attribute TO.P for the pin name
1329 {
1330 if( aTraceMode == FILLED )
1331 {
1332 #ifdef GBR_USE_MACROS_FOR_ROTATED_OVAL
1334 #endif
1335 {
1336 m_hasApertureRotOval = true;
1337 // We are using a aperture macro that expect size.y < size.x
1338 // i.e draw a horizontal line for rotation = 0.0
1339 // size.x = length, size.y = width
1340 if( size.x < size.y )
1341 {
1342 std::swap( size.x, size.y );
1343 orient += ANGLE_90;
1344
1345 if( orient > ANGLE_180 )
1346 orient -= ANGLE_180;
1347 }
1348
1349 VECTOR2D pos_device = userToDeviceCoordinates( aPos );
1350 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1351 selectAperture( size, 0, orient, APERTURE::AM_ROTATED_OVAL, aperture_attrib );
1352
1353 if( gbr_metadata )
1354 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1355
1356 emitDcode( pos_device, 3 );
1357 return;
1358 }
1359 // Draw the oval as round rect pad with a radius = 50% min size)
1360 // In gerber file, it will be drawn as a region with arcs, and can be
1361 // detected as pads (similar to a flashed pad)
1362 FlashPadRoundRect( aPos, aSize, std::min( aSize.x, aSize.y ) / 2, orient, FILLED,
1363 aData );
1364 }
1365 else // Non filled shape: plot outlines:
1366 {
1367 if( size.x > size.y )
1368 {
1369 std::swap( size.x, size.y );
1370
1371 if( orient < ANGLE_270 )
1372 orient += ANGLE_90;
1373 else
1374 orient -= ANGLE_270;
1375 }
1376
1377 sketchOval( aPos, size, orient, -1 );
1378 }
1379 }
1380}
1381
1382
1383void GERBER_PLOTTER::FlashPadRect( const VECTOR2I& pos, const VECTOR2I& aSize,
1384 const EDA_ANGLE& aOrient, OUTLINE_MODE aTraceMode, void* aData )
1385
1386{
1387 wxASSERT( m_outputFile );
1388
1389 VECTOR2I size( aSize );
1390 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1391
1392 // Horizontal / vertical rect can use a basic aperture (not a macro)
1393 // so use it for rotation n*90 deg
1394 if( aOrient.IsCardinal() )
1395 {
1396 if( aOrient.IsCardinal90() )
1397 // Build the not rotated equivalent shape:
1398 std::swap( size.x, size.y );
1399
1400 if( aTraceMode == SKETCH )
1401 {
1402 if( gbr_metadata )
1403 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1404
1406 Rect( VECTOR2I( pos.x - ( size.x / 2 ), pos.y - (size.y / 2 ) ),
1407 VECTOR2I( pos.x + ( size.x / 2 ), pos.y + (size.y / 2 ) ),
1408 FILL_T::NO_FILL, GetCurrentLineWidth() );
1409 }
1410 else
1411 {
1412 VECTOR2D pos_device = userToDeviceCoordinates( pos );
1413 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1414 selectAperture( size, 0, ANGLE_0, APERTURE::AT_RECT, aperture_attrib );
1415
1416 if( gbr_metadata )
1417 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1418
1419 emitDcode( pos_device, 3 );
1420 }
1421 }
1422 else
1423 {
1424 #ifdef GBR_USE_MACROS_FOR_ROTATED_RECT
1425 if( aTraceMode != SKETCH && !m_gerberDisableApertMacros )
1426 {
1427 m_hasApertureRotRect = true;
1428
1429 VECTOR2D pos_device = userToDeviceCoordinates( pos );
1430 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1431 selectAperture( size, 0, aOrient, APERTURE::AM_ROT_RECT, aperture_attrib );
1432
1433 if( gbr_metadata )
1434 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1435
1436 emitDcode( pos_device, 3 );
1437 }
1438 else
1439 #endif
1440 {
1441 // plot pad shape as Gerber region
1442 VECTOR2I coord[4];
1443 // coord[0] is assumed the lower left
1444 // coord[1] is assumed the upper left
1445 // coord[2] is assumed the upper right
1446 // coord[3] is assumed the lower right
1447
1448 coord[0].x = -size.x/2; // lower left
1449 coord[0].y = size.y/2;
1450 coord[1].x = -size.x/2; // upper left
1451 coord[1].y = -size.y/2;
1452 coord[2].x = size.x/2; // upper right
1453 coord[2].y = -size.y/2;
1454 coord[3].x = size.x/2; // lower right
1455 coord[3].y = size.y/2;
1456
1457 FlashPadTrapez( pos, coord, aOrient, aTraceMode, aData );
1458 }
1459 }
1460}
1461
1462void GERBER_PLOTTER::FlashPadRoundRect( const VECTOR2I& aPadPos, const VECTOR2I& aSize,
1463 int aCornerRadius, const EDA_ANGLE& aOrient,
1464 OUTLINE_MODE aTraceMode, void* aData )
1465
1466{
1467 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1468
1469 if( aTraceMode != FILLED )
1470 {
1471 SHAPE_POLY_SET outline;
1472 TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient, aCornerRadius, 0.0,
1474
1476
1477 std::vector<VECTOR2I> cornerList;
1478 // TransformRoundRectToPolygon creates only one convex polygon
1479 SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
1480 cornerList.reserve( poly.PointCount() + 1 );
1481
1482 for( int ii = 0; ii < poly.PointCount(); ++ii )
1483 cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
1484
1485 // Close polygon
1486 cornerList.push_back( cornerList[0] );
1487
1488 // plot outlines
1489 PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), gbr_metadata );
1490 }
1491 else
1492 {
1493 #ifdef GBR_USE_MACROS_FOR_ROUNDRECT
1495 #endif
1496 {
1498
1499 VECTOR2D pos_dev = userToDeviceCoordinates( aPadPos );
1500 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1501 selectAperture( aSize, aCornerRadius, aOrient, APERTURE::AM_ROUND_RECT,
1502 aperture_attrib );
1503
1504 if( gbr_metadata )
1505 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1506
1507 emitDcode( pos_dev, 3 );
1508 return;
1509 }
1510
1511 // A Pad RoundRect is plotted as a Gerber region.
1512 // Initialize region metadata:
1513 bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
1514
1515 if( gbr_metadata )
1516 {
1517 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1518 std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
1519
1520 if( !attrib.empty() )
1521 {
1522 fputs( attrib.c_str(), m_outputFile );
1523 clearTA_AperFunction = true;
1524 }
1525 }
1526
1527 // Plot the region using arcs in corners.
1528 plotRoundRectAsRegion( aPadPos, aSize, aCornerRadius, aOrient );
1529
1530 // Clear the TA attribute, to avoid the next item to inherit it:
1531 if( clearTA_AperFunction )
1532 {
1533 if( m_useX2format )
1534 fputs( "%TD.AperFunction*%\n", m_outputFile );
1535 else
1536 fputs( "G04 #@! TD.AperFunction*\n", m_outputFile );
1537 }
1538 }
1539}
1540
1541
1542void GERBER_PLOTTER::plotRoundRectAsRegion( const VECTOR2I& aRectCenter, const VECTOR2I& aSize,
1543 int aCornerRadius, const EDA_ANGLE& aOrient )
1544{
1545 // The region outline is generated by 4 sides and 4 90 deg arcs
1546 // 1 --- 2
1547 // | c |
1548 // 4 --- 3
1549
1550 // Note also in user coordinates the Y axis is from top to bottom
1551 // for historical reasons.
1552
1553 // A helper structure to handle outlines coordinates (segments and arcs)
1554 // in user coordinates
1555 struct RR_EDGE
1556 {
1557 VECTOR2I m_start;
1558 VECTOR2I m_end;
1559 VECTOR2I m_center;
1560 EDA_ANGLE m_arc_angle_start;
1561 };
1562
1563 int hsizeX = aSize.x/2;
1564 int hsizeY = aSize.y/2;
1565
1566 RR_EDGE curr_edge;
1567 std::vector<RR_EDGE> rr_outline;
1568
1569 rr_outline.reserve( 4 );
1570
1571 // Build outline coordinates, relative to rectangle center, rotation 0:
1572
1573 // Top left corner 1 (and 4 to 1 left vertical side @ x=-hsizeX)
1574 curr_edge.m_start.x = -hsizeX;
1575 curr_edge.m_start.y = hsizeY - aCornerRadius;
1576 curr_edge.m_end.x = curr_edge.m_start.x;
1577 curr_edge.m_end.y = -hsizeY + aCornerRadius;
1578 curr_edge.m_center.x = -hsizeX + aCornerRadius;
1579 curr_edge.m_center.y = curr_edge.m_end.y;
1580 curr_edge.m_arc_angle_start = aOrient + ANGLE_180;
1581
1582 rr_outline.push_back( curr_edge );
1583
1584 // Top right corner 2 (and 1 to 2 top horizontal side @ y=-hsizeY)
1585 curr_edge.m_start.x = -hsizeX + aCornerRadius;
1586 curr_edge.m_start.y = -hsizeY;
1587 curr_edge.m_end.x = hsizeX - aCornerRadius;
1588 curr_edge.m_end.y = curr_edge.m_start.y;
1589 curr_edge.m_center.x = curr_edge.m_end.x;
1590 curr_edge.m_center.y = -hsizeY + aCornerRadius;
1591 curr_edge.m_arc_angle_start = aOrient + ANGLE_90;
1592
1593 rr_outline.push_back( curr_edge );
1594
1595 // bottom right corner 3 (and 2 to 3 right vertical side @ x=hsizeX)
1596 curr_edge.m_start.x = hsizeX;
1597 curr_edge.m_start.y = -hsizeY + aCornerRadius;
1598 curr_edge.m_end.x = curr_edge.m_start.x;
1599 curr_edge.m_end.y = hsizeY - aCornerRadius;
1600 curr_edge.m_center.x = hsizeX - aCornerRadius;
1601 curr_edge.m_center.y = curr_edge.m_end.y;
1602 curr_edge.m_arc_angle_start = aOrient + ANGLE_0;
1603
1604 rr_outline.push_back( curr_edge );
1605
1606 // bottom left corner 4 (and 3 to 4 bottom horizontal side @ y=hsizeY)
1607 curr_edge.m_start.x = hsizeX - aCornerRadius;
1608 curr_edge.m_start.y = hsizeY;
1609 curr_edge.m_end.x = -hsizeX + aCornerRadius;
1610 curr_edge.m_end.y = curr_edge.m_start.y;
1611 curr_edge.m_center.x = curr_edge.m_end.x;
1612 curr_edge.m_center.y = hsizeY - aCornerRadius;
1613 curr_edge.m_arc_angle_start = aOrient - ANGLE_90;
1614
1615 rr_outline.push_back( curr_edge );
1616
1617 // Move relative coordinates to the actual location and rotation:
1618 VECTOR2I arc_last_center;
1619 EDA_ANGLE arc_last_angle = curr_edge.m_arc_angle_start - ANGLE_90;
1620
1621 for( RR_EDGE& rr_edge: rr_outline )
1622 {
1623 RotatePoint( rr_edge.m_start, aOrient );
1624 RotatePoint( rr_edge.m_end, aOrient );
1625 RotatePoint( rr_edge.m_center, aOrient );
1626 rr_edge.m_start += aRectCenter;
1627 rr_edge.m_end += aRectCenter;
1628 rr_edge.m_center += aRectCenter;
1629 arc_last_center = rr_edge.m_center;
1630 }
1631
1632 // Ensure the region is a closed polygon, i.e. the end point of last segment
1633 // (end of arc) is the same as the first point. Rounding issues can create a
1634 // small difference, mainly for rotated pads.
1635 // calculate last point (end of last arc):
1636 VECTOR2I last_pt;
1637 last_pt.x = arc_last_center.x + KiROUND( aCornerRadius * arc_last_angle.Cos() );
1638 last_pt.y = arc_last_center.y - KiROUND( aCornerRadius * arc_last_angle.Sin() );
1639
1640 VECTOR2I first_pt = rr_outline[0].m_start;
1641
1642#if 0 // For test only:
1643 if( last_pt != first_pt )
1644 wxLogMessage( wxS( "first pt %d %d last pt %d %d" ),
1645 first_pt.x, first_pt.y, last_pt.x, last_pt.y );
1646#endif
1647
1648 fputs( "G36*\n", m_outputFile ); // Start region
1649 fputs( "G01*\n", m_outputFile ); // Set linear interpolation.
1650 first_pt = last_pt;
1651 MoveTo( first_pt ); // Start point of region, must be same as end point
1652
1653 for( RR_EDGE& rr_edge: rr_outline )
1654 {
1655 if( aCornerRadius ) // Guard: ensure we do not create arcs with radius = 0
1656 {
1657 // LineTo( rr_edge.m_end ); // made in plotArc()
1658 plotArc( rr_edge.m_center, -rr_edge.m_arc_angle_start,
1659 -rr_edge.m_arc_angle_start + ANGLE_90, aCornerRadius, true );
1660 }
1661 else
1662 {
1663 LineTo( rr_edge.m_end );
1664 }
1665 }
1666
1667 fputs( "G37*\n", m_outputFile ); // Close region
1668}
1669
1670
1671void GERBER_PLOTTER::FlashPadCustom( const VECTOR2I& aPadPos, const VECTOR2I& aSize,
1672 const EDA_ANGLE& aOrient, SHAPE_POLY_SET* aPolygons,
1673 OUTLINE_MODE aTraceMode, void* aData )
1674
1675{
1676 // A Pad custom is plotted as polygon (a region in Gerber language).
1677 GBR_METADATA gbr_metadata;
1678
1679 if( aData )
1680 gbr_metadata = *static_cast<GBR_METADATA*>( aData );
1681
1682 SHAPE_POLY_SET polyshape = aPolygons->CloneDropTriangulation();
1683
1684 if( aTraceMode != FILLED )
1685 {
1687 }
1688
1689 std::vector<VECTOR2I> cornerList;
1690
1691 for( int cnt = 0; cnt < polyshape.OutlineCount(); ++cnt )
1692 {
1693 SHAPE_LINE_CHAIN& poly = polyshape.Outline( cnt );
1694
1695 cornerList.clear();
1696
1697 for( int ii = 0; ii < poly.PointCount(); ++ii )
1698 cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
1699
1700 // Close polygon
1701 cornerList.push_back( cornerList[0] );
1702
1703 if( aTraceMode == SKETCH )
1704 {
1705 PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
1706 }
1707 else
1708 {
1709#ifdef GBR_USE_MACROS_FOR_CUSTOM_PAD
1711 || cornerList.size() > GBR_MACRO_FOR_CUSTOM_PAD_MAX_CORNER_COUNT )
1712 {
1713 PlotGerberRegion( cornerList, &gbr_metadata );
1714 }
1715 else
1716 {
1717 // An AM will be created. the shape must be in position 0,0 and orientation 0
1718 // to be able to reuse the same AM for pads having the same shape
1719 for( size_t ii = 0; ii < cornerList.size(); ii++ )
1720 {
1721 cornerList[ii] -= aPadPos;
1722 RotatePoint( cornerList[ii], -aOrient );
1723 }
1724
1725 VECTOR2D pos_dev = userToDeviceCoordinates( aPadPos );
1726 selectAperture( cornerList, aOrient, APERTURE::AM_FREE_POLYGON,
1727 gbr_metadata.GetApertureAttrib() );
1728 formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1729
1730 emitDcode( pos_dev, 3 );
1731 }
1732#else
1733 PlotGerberRegion( cornerList, &gbr_metadata );
1734#endif
1735 }
1736 }
1737}
1738
1739
1740void GERBER_PLOTTER::FlashPadChamferRoundRect( const VECTOR2I& aShapePos, const VECTOR2I& aPadSize,
1741 int aCornerRadius, double aChamferRatio,
1742 int aChamferPositions, const EDA_ANGLE& aPadOrient,
1743 OUTLINE_MODE aPlotMode, void* aData )
1744
1745{
1746 GBR_METADATA gbr_metadata;
1747
1748 if( aData )
1749 gbr_metadata = *static_cast<GBR_METADATA*>( aData );
1750
1751 VECTOR2D pos_device = userToDeviceCoordinates( aShapePos );
1752 SHAPE_POLY_SET outline;
1753 std::vector<VECTOR2I> cornerList;
1754
1755 bool hasRoundedCorner = aCornerRadius != 0 && aChamferPositions != 15;
1756
1757#ifdef GBR_USE_MACROS_FOR_CHAMFERED_RECT
1758 // Sketch mode or round rect shape or Apert Macros disabled
1759 if( aPlotMode != FILLED || hasRoundedCorner || m_gerberDisableApertMacros )
1760#endif
1761 {
1762 TransformRoundChamferedRectToPolygon( outline, aShapePos, aPadSize, aPadOrient,
1763 aCornerRadius, aChamferRatio, aChamferPositions, 0,
1765
1766 // Build the corner list
1767 const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
1768
1769 for( int ii = 0; ii < corners.PointCount(); ii++ )
1770 cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
1771
1772 // Close the polygon
1773 cornerList.push_back( cornerList[0] );
1774
1775 if( aPlotMode == SKETCH )
1776 PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
1777 else
1778 {
1779#ifdef GBR_USE_MACROS_FOR_CHAMFERED_ROUND_RECT
1781 {
1782 PlotGerberRegion( cornerList, &gbr_metadata );
1783 }
1784 else
1785 {
1786 // An AM will be created. the shape must be in position 0,0 and orientation 0
1787 // to be able to reuse the same AM for pads having the same shape
1788 for( size_t ii = 0; ii < cornerList.size(); ii++ )
1789 {
1790 cornerList[ii] -= aShapePos;
1791 RotatePoint( cornerList[ii], -aPadOrient );
1792 }
1793
1794 selectAperture( cornerList, aPadOrient, APERTURE::AM_FREE_POLYGON,
1795 gbr_metadata.GetApertureAttrib() );
1796 formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1797
1798 emitDcode( pos_device, 3 );
1799 }
1800#else
1801 PlotGerberRegion( cornerList, &gbr_metadata );
1802#endif
1803 }
1804
1805 return;
1806 }
1807
1808 // Build the chamfered polygon (4 to 8 corners )
1809 TransformRoundChamferedRectToPolygon( outline, VECTOR2I( 0, 0 ), aPadSize, ANGLE_0, 0,
1810 aChamferRatio, aChamferPositions, 0,
1812
1813 // Build the corner list
1814 const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
1815
1816 // Generate the polygon (4 to 8 corners )
1817 for( int ii = 0; ii < corners.PointCount(); ii++ )
1818 cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
1819
1820 switch( cornerList.size() )
1821 {
1822 case 4:
1824 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE4P,
1825 gbr_metadata.GetApertureAttrib() );
1826 break;
1827
1828 case 5:
1830 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE5P,
1831 gbr_metadata.GetApertureAttrib() );
1832 break;
1833
1834 case 6:
1836 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE6P,
1837 gbr_metadata.GetApertureAttrib() );
1838 break;
1839
1840 case 7:
1842 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE7P,
1843 gbr_metadata.GetApertureAttrib() );
1844 break;
1845
1846 case 8:
1848 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE8P,
1849 gbr_metadata.GetApertureAttrib() );
1850 break;
1851
1852 default:
1853 wxLogMessage( wxS( "FlashPadChamferRoundRect(): Unexpected number of corners (%d)" ),
1854 (int)cornerList.size() );
1855 break;
1856 }
1857
1858 formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1859
1860 emitDcode( pos_device, 3 );
1861}
1862
1863
1864void GERBER_PLOTTER::FlashPadTrapez( const VECTOR2I& aPadPos, const VECTOR2I* aCorners,
1865 const EDA_ANGLE& aPadOrient, OUTLINE_MODE aTraceMode,
1866 void* aData )
1867
1868{
1869 // polygon corners list
1870 std::vector<VECTOR2I> cornerList = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
1871
1872 // Draw the polygon and fill the interior as required
1873 for( unsigned ii = 0; ii < 4; ii++ )
1874 {
1875 RotatePoint( cornerList[ii], aPadOrient );
1876 cornerList[ii] += aPadPos;
1877 }
1878
1879 // Close the polygon
1880 cornerList.push_back( cornerList[0] );
1881
1882 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1883 GBR_METADATA metadata;
1884
1885 if( gbr_metadata )
1886 metadata = *gbr_metadata;
1887
1888 if( aTraceMode == SKETCH )
1889 {
1890 PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), &metadata );
1891 return;
1892 }
1893
1894 // Plot a filled polygon:
1895 #ifdef GBR_USE_MACROS_FOR_TRAPEZOID
1897 #endif
1898 {
1900 VECTOR2D pos_dev = userToDeviceCoordinates( aPadPos );
1901 // polygon corners list
1902 std::vector<VECTOR2I> corners = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
1903 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1904 selectAperture( corners, aPadOrient, APERTURE::APER_MACRO_OUTLINE4P, aperture_attrib );
1905
1906 if( gbr_metadata )
1907 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1908
1909 emitDcode( pos_dev, 3 );
1910 return;
1911 }
1912
1913 PlotGerberRegion( cornerList, &metadata );
1914}
1915
1916
1917void GERBER_PLOTTER::FlashRegularPolygon( const VECTOR2I& aShapePos, int aDiameter,
1918 int aCornerCount, const EDA_ANGLE& aOrient,
1919 OUTLINE_MODE aTraceMode, void* aData )
1920{
1921 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1922
1923 GBR_METADATA metadata;
1924
1925 if( gbr_metadata )
1926 metadata = *gbr_metadata;
1927
1928 if( aTraceMode == SKETCH )
1929 {
1930 // Build the polygon:
1931 std::vector<VECTOR2I> cornerList;
1932
1933 EDA_ANGLE angle_delta = ANGLE_360 / aCornerCount;
1934
1935 for( int ii = 0; ii < aCornerCount; ii++ )
1936 {
1937 EDA_ANGLE rot = aOrient + ( angle_delta * ii );
1938 VECTOR2I vertice( aDiameter / 2, 0 );
1939
1940 RotatePoint( vertice, rot );
1941 vertice += aShapePos;
1942 cornerList.push_back( vertice );
1943 }
1944
1945 cornerList.push_back( cornerList[0] ); // Close the shape
1946
1947 PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
1948 }
1949 else
1950 {
1951 VECTOR2D pos_dev = userToDeviceCoordinates( aShapePos );
1952 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1953
1954 APERTURE::APERTURE_TYPE apert_type =
1956 selectAperture( aDiameter, aOrient, apert_type, aperture_attrib );
1957
1958 if( gbr_metadata )
1959 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1960
1961 emitDcode( pos_dev, 3 );
1962 }
1963}
1964
1965
1967 const COLOR4D& aColor,
1968 const wxString& aText,
1969 const EDA_ANGLE& aOrient,
1970 const VECTOR2I& aSize,
1971 enum GR_TEXT_H_ALIGN_T aH_justify,
1972 enum GR_TEXT_V_ALIGN_T aV_justify,
1973 int aWidth,
1974 bool aItalic,
1975 bool aBold,
1976 bool aMultilineAllowed,
1977 KIFONT::FONT* aFont,
1978 const KIFONT::METRICS& aFontMetrics,
1979 void* aData )
1980{
1981 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1982
1983 if( gbr_metadata )
1984 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1985
1986 PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth,
1987 aItalic, aBold, aMultilineAllowed, aFont, aFontMetrics, aData );
1988}
1989
1990
1992 const COLOR4D& aColor,
1993 const wxString& aText,
1994 const TEXT_ATTRIBUTES& aAttributes,
1995 KIFONT::FONT* aFont,
1996 const KIFONT::METRICS& aFontMetrics,
1997 void* aData )
1998{
1999 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
2000
2001 if( gbr_metadata )
2002 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
2003
2004 PLOTTER::PlotText( aPos, aColor, aText, aAttributes, aFont, aFontMetrics, aData );
2005}
2006
2008{
2009 if( aPositive )
2010 fprintf( m_outputFile, "%%LPD*%%\n" );
2011 else
2012 fprintf( m_outputFile, "%%LPC*%%\n" );
2013}
2014
2015
2016bool APER_MACRO_FREEPOLY::IsSamePoly( const std::vector<VECTOR2I>& aPolygon ) const
2017{
2018 return polyCompare( m_Corners, aPolygon );
2019}
2020
2021
2022void APER_MACRO_FREEPOLY::Format( FILE * aOutput, double aIu2GbrMacroUnit )
2023{
2024 // Write aperture header
2025 fprintf( aOutput, "%%AM%s%d*\n", AM_FREEPOLY_BASENAME, m_Id );
2026 fprintf( aOutput, "4,1,%d,", (int)m_Corners.size() );
2027
2028 // Insert a newline after curr_line_count_max coordinates.
2029 int curr_line_corner_count = 0;
2030 const int curr_line_count_max = 20; // <= 0 to disable newlines
2031
2032 for( size_t ii = 0; ii <= m_Corners.size(); ii++ )
2033 {
2034 int jj = ii;
2035
2036 if( ii >= m_Corners.size() )
2037 jj = 0;
2038
2039 // Note: parameter values are always mm or inches
2040 fprintf( aOutput, "%#f,%#f,",
2041 m_Corners[jj].x * aIu2GbrMacroUnit, -m_Corners[jj].y * aIu2GbrMacroUnit );
2042
2043 if( curr_line_count_max >= 0 && ++curr_line_corner_count >= curr_line_count_max )
2044 {
2045 fprintf( aOutput, "\n" );
2046 curr_line_corner_count = 0;
2047 }
2048 }
2049
2050 // output rotation parameter
2051 fputs( "$1*%\n", aOutput );
2052}
2053
2054
2055void APER_MACRO_FREEPOLY_LIST::Format( FILE * aOutput, double aIu2GbrMacroUnit )
2056{
2057 for( int idx = 0; idx < AmCount(); idx++ )
2058 m_AMList[idx].Format( aOutput, aIu2GbrMacroUnit );
2059}
2060
2061
2062void APER_MACRO_FREEPOLY_LIST::Append( const std::vector<VECTOR2I>& aPolygon )
2063{
2064 m_AMList.emplace_back( aPolygon, AmCount() );
2065}
2066
2067
2068int APER_MACRO_FREEPOLY_LIST::FindAm( const std::vector<VECTOR2I>& aPolygon ) const
2069{
2070 for( int idx = 0; idx < AmCount(); idx++ )
2071 {
2072 if( m_AMList[idx].IsSamePoly( aPolygon ) )
2073 return idx;
2074 }
2075
2076 return -1;
2077}
#define GBR_MACRO_FOR_CUSTOM_PAD_MAX_CORNER_COUNT
static bool polyCompare(const std::vector< VECTOR2I > &aPolygon, const std::vector< VECTOR2I > &aTestPolygon)
#define AM_FREEPOLY_BASENAME
wxString GetBuildVersion()
Get the full KiCad version string.
EDA_ANGLE m_Rotation
std::vector< VECTOR2I > m_Corners
APERTURE_TYPE m_Type
std::vector< APER_MACRO_FREEPOLY > m_AMList
void Append(const std::vector< VECTOR2I > &aPolygon)
append a new APER_MACRO_FREEPOLY containing the polygon aPolygon to the current list
int FindAm(const std::vector< VECTOR2I > &aPolygon) const
void Format(FILE *aOutput, double aIu2GbrMacroUnit)
print the aperture macro list to aOutput
void Format(FILE *aOutput, double aIu2GbrMacroUnit)
print the aperture macro definition to aOutput
bool IsSamePoly(const std::vector< VECTOR2I > &aPolygon) const
std::vector< VECTOR2I > m_Corners
EDA_ANGLE Normalize()
Definition: eda_angle.h:255
double Sin() const
Definition: eda_angle.h:212
double AsDegrees() const
Definition: eda_angle.h:155
bool IsCardinal() const
Definition: eda_angle.cpp:40
double AsRadians() const
Definition: eda_angle.h:159
bool IsCardinal90() const
Definition: eda_angle.cpp:54
double Cos() const
Definition: eda_angle.h:227
static std::string FormatAttribute(GBR_APERTURE_ATTRIB aAttribute, bool aUseX1StructuredComment)
Metadata which can be added in a gerber file as attribute in X2 format.
Definition: gbr_metadata.h:205
GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB GetApertureAttrib()
Definition: gbr_metadata.h:214
GBR_NETLIST_METADATA m_NetlistMetadata
An item to handle object attribute.
Definition: gbr_metadata.h:262
GBR_APERTURE_METADATA m_ApertureMetadata
An item to handle aperture attribute.
Definition: gbr_metadata.h:257
Information which can be added in a gerber file as attribute of an object.
wxString m_ExtraData
a string to print after TO object attributes, if not empty it is printed "as this"
virtual void Circle(const VECTOR2I &pos, int diametre, FILL_T fill, int width=USE_DEFAULT_LINE_WIDTH) override
std::string m_objectAttributesDictionary
virtual void SetGerberCoordinatesFormat(int aResolution, bool aUseInches=false) override
Selection of Gerber units and resolution (number of digits in mantissa).
virtual void ThickArc(const VECTOR2D &aCentre, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aAngle, double aRadius, int aWidth, OUTLINE_MODE aTraceMode, void *aData) override
virtual void ThickCircle(const VECTOR2I &pos, int diametre, int width, OUTLINE_MODE tracemode, void *aData) override
void ClearAllAttributes()
Remove (clear) all attributes from object attributes dictionary (TO.
void selectAperture(const VECTOR2I &aSize, int aRadius, const EDA_ANGLE &aRotation, APERTURE::APERTURE_TYPE aType, int aApertureAttribute)
Pick an existing aperture or create a new one, matching the size, type and attributes.
virtual void FlashPadCustom(const VECTOR2I &aPadPos, const VECTOR2I &aSize, const EDA_ANGLE &aPadOrient, SHAPE_POLY_SET *aPolygons, OUTLINE_MODE aTraceMode, void *aData) override
int GetOrCreateAperture(const VECTOR2I &aSize, int aRadius, const EDA_ANGLE &aRotation, APERTURE::APERTURE_TYPE aType, int aApertureAttribute)
void PlotPolyAsRegion(const SHAPE_LINE_CHAIN &aPoly, FILL_T aFill, int aWidth, GBR_METADATA *aGbrMetadata)
Similar to PlotPoly(), plot a filled polygon using Gerber region, therefore adding X2 attributes to t...
virtual void PenTo(const VECTOR2I &pos, char plume) override
Moveto/lineto primitive, moves the 'pen' to the specified direction.
void emitDcode(const VECTOR2D &pt, int dcode)
Emit a D-Code record, using proper conversions to format a leading zero omitted gerber coordinate.
APER_MACRO_FREEPOLY_LIST m_am_freepoly_list
virtual void Rect(const VECTOR2I &p1, const VECTOR2I &p2, FILL_T fill, int width=USE_DEFAULT_LINE_WIDTH) override
virtual void FlashPadCircle(const VECTOR2I &pos, int diametre, OUTLINE_MODE trace_mode, void *aData) override
Filled circular flashes are stored as apertures.
std::vector< APERTURE > m_apertures
bool m_hasApertureChamferedRect
virtual void FlashPadTrapez(const VECTOR2I &aPadPos, const VECTOR2I *aCorners, const EDA_ANGLE &aPadOrient, OUTLINE_MODE aTraceMode, void *aData) override
Flash a trapezoidal pad.
bool m_hasApertureOutline4P
virtual void SetCurrentLineWidth(int aLineWidth, void *aData=nullptr) override
Set the line width for the next drawing.
void writeApertureList()
Generate the table of D codes.
void plotRoundRectAsRegion(const VECTOR2I &aRectCenter, const VECTOR2I &aSize, int aCornerRadius, const EDA_ANGLE &aOrient)
Plot a round rect (a round rect shape in fact) as a Gerber region using lines and arcs for corners.
virtual void PlotText(const VECTOR2I &aPos, const COLOR4D &aColor, const wxString &aText, const TEXT_ATTRIBUTES &aAttributes, KIFONT::FONT *aFont, const KIFONT::METRICS &aFontMetrics, void *aData=nullptr) override
virtual void FlashPadRect(const VECTOR2I &aPadPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, OUTLINE_MODE aTraceMode, void *aData) override
void FlashPadChamferRoundRect(const VECTOR2I &aShapePos, const VECTOR2I &aPadSize, int aCornerRadius, double aChamferRatio, int aChamferPositions, const EDA_ANGLE &aPadOrient, OUTLINE_MODE aPlotMode, void *aData)
Flash a chamfered round rect pad.
wxString m_workFilename
bool m_hasApertureRoundRect
bool m_gerberDisableApertMacros
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 formatNetAttribute(GBR_NETLIST_METADATA *aData)
Print a Gerber net attribute object record.
virtual void SetLayerPolarity(bool aPositive) override
Change the plot polarity and begin a new layer.
virtual void FlashPadOval(const VECTOR2I &aPadPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, OUTLINE_MODE aTraceMode, void *aData) override
virtual void PlotPoly(const std::vector< VECTOR2I > &aCornerList, FILL_T aFill, int aWidth=USE_DEFAULT_LINE_WIDTH, void *aData=nullptr) override
Gerber polygon: they can (and should) be filled with the appropriate G36/G37 sequence.
virtual void FlashPadRoundRect(const VECTOR2I &aPadPos, const VECTOR2I &aSize, int aCornerRadius, const EDA_ANGLE &aOrient, OUTLINE_MODE aTraceMode, void *aData) override
void PlotGerberRegion(const std::vector< VECTOR2I > &aCornerList, GBR_METADATA *aGbrMetadata)
Plot a Gerber region: similar to PlotPoly but plot only filled polygon, and add the TA....
virtual void EndBlock(void *aData) override
Define the end of a group of drawing items the group is started by StartBlock().
virtual void ThickRect(const VECTOR2I &p1, const VECTOR2I &p2, int width, OUTLINE_MODE tracemode, void *aData) override
virtual void ThickSegment(const VECTOR2I &start, const VECTOR2I &end, int width, OUTLINE_MODE tracemode, void *aData) override
void clearNetAttribute()
Clear a Gerber net attribute record (clear object attribute dictionary) and output the clear object a...
virtual void StartBlock(void *aData) override
Calling this function allows one to define the beginning of a group of drawing items (used in X2 form...
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.
virtual void FilledCircle(const VECTOR2I &pos, int diametre, OUTLINE_MODE tracemode, void *aData) override
virtual void Arc(const VECTOR2D &aCenter, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aAngle, double aRadius, FILL_T aFill, int aWidth=USE_DEFAULT_LINE_WIDTH) override
void plotArc(const VECTOR2I &aCenter, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aEndAngle, double aRadius, bool aPlotInRegion)
Plot a Gerber arc.
virtual void Text(const VECTOR2I &aPos, const COLOR4D &aColor, const wxString &aText, const EDA_ANGLE &aOrient, const VECTOR2I &aSize, enum GR_TEXT_H_ALIGN_T aH_justify, enum GR_TEXT_V_ALIGN_T aV_justify, int aWidth, bool aItalic, bool aBold, bool aMultilineAllowed, KIFONT::FONT *aFont, const KIFONT::METRICS &aFontMetrics, void *aData=nullptr) override
Draw text with the plotter.
FONT is an abstract base class for both outline and stroke fonts.
Definition: font.h:131
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
int GetDefaultPenWidth() const
void SetDefaultPenWidth(int aWidth)
wxArrayString m_headerExtraLines
Definition: plotter.h:678
bool m_plotMirror
Definition: plotter.h:655
static const int USE_DEFAULT_LINE_WIDTH
Definition: plotter.h:108
virtual void polyArc(const VECTOR2D &aCentre, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aAngle, double aRadius, FILL_T aFill, int aWidth=USE_DEFAULT_LINE_WIDTH)
Generic fallback: arc rendered as a polyline.
Definition: plotter.cpp:180
void MoveTo(const VECTOR2I &pos)
Definition: plotter.h:243
void FinishTo(const VECTOR2I &pos)
Definition: plotter.h:253
double m_iuPerDeviceUnit
Definition: plotter.h:652
VECTOR2I m_plotOffset
Definition: plotter.h:654
virtual VECTOR2D userToDeviceCoordinates(const VECTOR2I &aCoordinate)
Modify coordinates according to the orientation, scale factor, and offsets trace.
Definition: plotter.cpp:90
VECTOR2I m_paperSize
Definition: plotter.h:676
void sketchOval(const VECTOR2I &aPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, int aWidth)
Definition: plotter.cpp:501
int GetPlotterArcHighDef() const
Definition: plotter.h:208
char m_penState
Definition: plotter.h:667
wxString m_creator
Definition: plotter.h:670
int m_currentPenWidth
Definition: plotter.h:666
double m_plotScale
Plot scale - chosen by the user (even implicitly with 'fit in a4')
Definition: plotter.h:644
FILE * m_outputFile
Output file.
Definition: plotter.h:661
void LineTo(const VECTOR2I &pos)
Definition: plotter.h:248
void PenFinish()
Definition: plotter.h:259
static const int DO_NOT_SET_LINE_WIDTH
Definition: plotter.h:107
RENDER_SETTINGS * m_renderSettings
Definition: plotter.h:680
virtual void Text(const VECTOR2I &aPos, const COLOR4D &aColor, const wxString &aText, const EDA_ANGLE &aOrient, const VECTOR2I &aSize, enum GR_TEXT_H_ALIGN_T aH_justify, enum GR_TEXT_V_ALIGN_T aV_justify, int aPenWidth, bool aItalic, bool aBold, bool aMultilineAllowed, KIFONT::FONT *aFont, const KIFONT::METRICS &aFontMetrics, void *aData=nullptr)
Draw text with the plotter.
Definition: plotter.cpp:691
void segmentAsOval(const VECTOR2I &start, const VECTOR2I &end, int width, OUTLINE_MODE tracemode)
Convert a thick segment and plot it as an oval.
Definition: plotter.cpp:486
double m_IUsPerDecimil
Definition: plotter.h:650
virtual void PlotText(const VECTOR2I &aPos, const COLOR4D &aColor, const wxString &aText, const TEXT_ATTRIBUTES &aAttributes, KIFONT::FONT *aFont, const KIFONT::METRICS &aFontMetrics, void *aData=nullptr)
Definition: plotter.cpp:753
virtual int GetCurrentLineWidth() const
Definition: plotter.h:147
const VECTOR2I & GetArcMid() const
Definition: shape_arc.h:115
bool IsClockwise() const
Definition: shape_arc.cpp:385
const VECTOR2I & GetP1() const
Definition: shape_arc.h:114
const VECTOR2I & GetP0() const
Definition: shape_arc.h:113
const VECTOR2I & GetCenter() const
Definition: shape_arc.cpp:475
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_ARC & Arc(size_t aArc) const
bool IsClosed() const override
int PointCount() const
Return the number of points (vertices) in this line chain.
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
const std::vector< VECTOR2I > & CPoints() const
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.
SHAPE_POLY_SET CloneDropTriangulation() const
void TransformRoundChamferedRectToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aPosition, const VECTOR2I &aSize, const EDA_ANGLE &aRotation, int aCornerRadius, double aChamferRatio, int aChamferCorners, int aInflate, int aError, ERROR_LOC aErrorLoc)
Convert a rectangle with rounded corners and/or chamfered corners to a polygon.
static constexpr EDA_ANGLE ANGLE_0
Definition: eda_angle.h:435
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:437
static constexpr EDA_ANGLE ANGLE_270
Definition: eda_angle.h:440
static constexpr EDA_ANGLE ANGLE_360
Definition: eda_angle.h:441
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:439
FILL_T
Definition: eda_shape.h:55
bool FormatNetAttribute(std::string &aPrintedText, std::string &aLastNetAttributes, const GBR_NETLIST_METADATA *aData, bool &aClearPreviousAttributes, bool aUseX1StructuredComment)
Generate the string to set a net attribute for a graphic object to print to a gerber file.
Handle special data (items attributes) during plot.
specialized plotter for GERBER files format
#define APER_MACRO_OUTLINE6P_NAME
#define APER_MACRO_OUTLINE4P_NAME
#define APER_MACRO_OUTLINE4P_HEADER
#define APER_MACRO_OUTLINE6P_HEADER
#define APER_MACRO_ROT_RECT_HEADER
#define APER_MACRO_OUTLINE8P_HEADER
#define APER_MACRO_SHAPE_OVAL_HEADER
#define APER_MACRO_ROT_RECT_NAME
#define APER_MACRO_SHAPE_OVAL_NAME
#define APER_MACRO_OUTLINE5P_HEADER
#define APER_MACRO_OUTLINE5P_NAME
#define APER_MACRO_ROUNDRECT_HEADER
#define APER_MACRO_OUTLINE8P_NAME
#define APER_MACRO_OUTLINE7P_HEADER
#define APER_MACRO_ROUNDRECT_NAME
#define APER_MACRO_OUTLINE7P_NAME
@ ERROR_INSIDE
static const bool FILLED
Definition: gr_basic.cpp:30
This file contains miscellaneous commonly used macros and functions.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:424
OUTLINE_MODE
Definition: outline_mode.h:25
@ SKETCH
Definition: outline_mode.h:26
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: string_utils.h:391
GR_TEXT_H_ALIGN_T
GR_TEXT_V_ALIGN_T
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition: trigo.cpp:228
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:118
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588