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 The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
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
161
163{
164 // Remove all attributes from object attributes dictionary (TO. and TA commands)
165 if( m_useX2format )
166 fputs( "%TD*%\n", m_outputFile );
167 else
168 fputs( "G04 #@! TD*\n", m_outputFile );
169
171}
172
173
175{
176 // disable a Gerber net attribute (exists only in X2 with net attributes mode).
177 if( m_objectAttributesDictionary.empty() ) // No net attribute or not X2 mode
178 return;
179
180 // Remove all net attributes from object attributes dictionary
181 if( m_useX2format )
182 fputs( "%TD*%\n", m_outputFile );
183 else
184 fputs( "G04 #@! TD*\n", m_outputFile );
185
187}
188
189
190void GERBER_PLOTTER::StartBlock( void* aData )
191{
192 // Currently, it is the same as EndBlock(): clear all aperture net attributes
193 EndBlock( aData );
194}
195
196
197void GERBER_PLOTTER::EndBlock( void* aData )
198{
199 // Remove all net attributes from object attributes dictionary
201}
202
203
205{
206 // print a Gerber net attribute record.
207 // it is added to the object attributes dictionary
208 // On file, only modified or new attributes are printed.
209 if( aData == nullptr )
210 return;
211
212 if( !m_useNetAttributes )
213 return;
214
215 bool useX1StructuredComment = !m_useX2format;
216
217 bool clearDict;
218 std::string short_attribute_string;
219
220 if( !FormatNetAttribute( short_attribute_string, m_objectAttributesDictionary,
221 aData, clearDict, useX1StructuredComment ) )
222 return;
223
224 if( clearDict )
226
227 if( !short_attribute_string.empty() )
228 fputs( short_attribute_string.c_str(), m_outputFile );
229
230 if( m_useX2format && !aData->m_ExtraData.IsEmpty() )
231 {
232 std::string extra_data = TO_UTF8( aData->m_ExtraData );
233 fputs( extra_data.c_str(), m_outputFile );
234 }
235}
236
237
238bool GERBER_PLOTTER::StartPlot( const wxString& aPageNumber )
239{
240 m_hasApertureRoundRect = false; // true is at least one round rect aperture is in use
241 m_hasApertureRotOval = false; // true is at least one oval rotated aperture is in use
242 m_hasApertureRotRect = false; // true is at least one rect. rotated aperture is in use
243 m_hasApertureOutline4P = false; // true is at least one rotated rect/trapezoid aperture
244 // is in use
245 m_hasApertureChamferedRect = false; // true is at least one chamfered rect is in use
247
248 wxASSERT( m_outputFile );
249
250 finalFile = m_outputFile; // the actual gerber file will be created later
251
252 // Create a temp file in system temp to avoid potential network share buffer issues for
253 // the final read and save.
254 m_workFilename = wxFileName::CreateTempFileName( "" );
255 workFile = wxFopen( m_workFilename, wxT( "wt" ) );
257 wxASSERT( m_outputFile );
258
259 if( m_outputFile == nullptr )
260 return false;
261
262 for( unsigned ii = 0; ii < m_headerExtraLines.GetCount(); ii++ )
263 {
264 if( ! m_headerExtraLines[ii].IsEmpty() )
265 fprintf( m_outputFile, "%s\n", TO_UTF8( m_headerExtraLines[ii] ) );
266 }
267
268 // Set coordinate format to 3.6 or 4.5 absolute, leading zero omitted
269 // the number of digits for the integer part of coordinates is needed
270 // in gerber format, but is not very important when omitting leading zeros
271 // It is fixed here to 3 (inch) or 4 (mm), but is not actually used
272 int leadingDigitCount = m_gerberUnitInch ? 3 : 4;
273
274 fprintf( m_outputFile, "%%FSLAX%d%dY%d%d*%%\n",
275 leadingDigitCount, m_gerberUnitFmt,
276 leadingDigitCount, m_gerberUnitFmt );
277 fprintf( m_outputFile,
278 "G04 Gerber Fmt %d.%d, Leading zero omitted, Abs format (unit %s)*\n",
279 leadingDigitCount, m_gerberUnitFmt,
280 m_gerberUnitInch ? "inch" : "mm" );
281
282 wxString Title = m_creator + wxT( " " ) + GetBuildVersion();
283
284 // In gerber files, ASCII7 chars only are allowed.
285 // So use a ISO date format (using a space as separator between date and time),
286 // not a localized date format
287 wxDateTime date = wxDateTime::Now();
288 fprintf( m_outputFile, "G04 Created by KiCad (%s) date %s*\n",
289 TO_UTF8( Title ), TO_UTF8( date.FormatISOCombined( ' ') ) );
290
291 /* Mass parameter: unit = INCHES/MM */
292 if( m_gerberUnitInch )
293 fputs( "%MOIN*%\n", m_outputFile );
294 else
295 fputs( "%MOMM*%\n", m_outputFile );
296
297 // Be sure the usual dark polarity is selected:
298 fputs( "%LPD*%\n", m_outputFile );
299
300 // Set initial interpolation mode: always G01 (linear):
301 fputs( "G01*\n", m_outputFile );
302
303 // Add aperture list start point
304 fputs( "G04 APERTURE LIST*\n", m_outputFile );
305
306 // Give a minimal value to the default pen size, used to plot items in sketch mode
307 if( m_renderSettings )
308 {
309 const int pen_min = 0.1 * m_IUsPerDecimil * 10000 / 25.4; // for min width = 0.1 mm
311 pen_min ) );
312 }
313
314 return true;
315}
316
317
319{
320 char line[1024];
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 );
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:
346 {
347 fputs( "G04 Aperture macros list*\n", m_outputFile );
348
351
354
357
360
362 {
367 }
368
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
379 }
380
381 fputs( "G04 Aperture macros list end*\n", m_outputFile );
382 }
383
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
398void 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 )
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
411 aperture_attribute );
412 m_currentPenWidth = aWidth;
413}
414
415
416int GERBER_PLOTTER::GetOrCreateAperture( const VECTOR2I& aSize, int aRadius,
417 const EDA_ANGLE& aRotation, APERTURE::APERTURE_TYPE aType,
418 int aApertureAttribute )
419{
420 int last_D_code = 9;
421
422 // Search an existing aperture
423 for( int idx = 0; idx < (int)m_apertures.size(); ++idx )
424 {
425 APERTURE* tool = &m_apertures[idx];
426 last_D_code = tool->m_DCode;
427
428 if( (tool->m_Type == aType) && (tool->m_Size == aSize) &&
429 (tool->m_Radius == aRadius) && (tool->m_Rotation == aRotation) &&
430 (tool->m_ApertureAttribute == aApertureAttribute) )
431 return idx;
432 }
433
434 // Allocate a new aperture
435 APERTURE new_tool;
436 new_tool.m_Size = aSize;
437 new_tool.m_Type = aType;
438 new_tool.m_Radius = aRadius;
439 new_tool.m_Rotation = aRotation;
440 new_tool.m_DCode = last_D_code + 1;
441 new_tool.m_ApertureAttribute = aApertureAttribute;
442
443 m_apertures.push_back( new_tool );
444
445 return m_apertures.size() - 1;
446}
447
448
449int GERBER_PLOTTER::GetOrCreateAperture( const std::vector<VECTOR2I>& aCorners,
450 const EDA_ANGLE& aRotation, APERTURE::APERTURE_TYPE aType,
451 int aApertureAttribute )
452{
453 int last_D_code = 9;
454
455 // For APERTURE::AM_FREE_POLYGON aperture macros, we need to create the macro
456 // on the fly, because due to the fact the vertex count is not a constant we
457 // cannot create a static definition.
458 if( APERTURE::AM_FREE_POLYGON == aType )
459 {
460 int idx = m_am_freepoly_list.FindAm( aCorners );
461
462 if( idx < 0 )
463 m_am_freepoly_list.Append( aCorners );
464 }
465
466 // Search an existing aperture
467 for( int idx = 0; idx < (int)m_apertures.size(); ++idx )
468 {
469 APERTURE* tool = &m_apertures[idx];
470
471 last_D_code = tool->m_DCode;
472
473 if( (tool->m_Type == aType) &&
474 (tool->m_Corners.size() == aCorners.size() ) &&
475 (tool->m_Rotation == aRotation) &&
476 (tool->m_ApertureAttribute == aApertureAttribute) )
477 {
478 // A candidate is found. the corner lists must be similar
479 bool is_same = polyCompare( tool->m_Corners, aCorners );
480
481 if( is_same )
482 return idx;
483 }
484 }
485
486 // Allocate a new aperture
487 APERTURE new_tool;
488
489 new_tool.m_Corners = aCorners;
490 new_tool.m_Size = VECTOR2I( 0, 0 ); // Not used
491 new_tool.m_Type = aType;
492 new_tool.m_Radius = 0; // Not used
493 new_tool.m_Rotation = aRotation;
494 new_tool.m_DCode = last_D_code + 1;
495 new_tool.m_ApertureAttribute = aApertureAttribute;
496
497 m_apertures.push_back( new_tool );
498
499 return m_apertures.size() - 1;
500}
501
502
503void GERBER_PLOTTER::selectAperture( const VECTOR2I& aSize, int aRadius, const EDA_ANGLE& aRotation,
504 APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
505{
506 bool change = ( m_currentApertureIdx < 0 ) ||
507 ( m_apertures[m_currentApertureIdx].m_Type != aType ) ||
508 ( m_apertures[m_currentApertureIdx].m_Size != aSize ) ||
509 ( m_apertures[m_currentApertureIdx].m_Radius != aRadius ) ||
510 ( m_apertures[m_currentApertureIdx].m_Rotation != aRotation );
511
512 if( !change )
513 change = m_apertures[m_currentApertureIdx].m_ApertureAttribute != aApertureAttribute;
514
515 if( change )
516 {
517 // Pick an existing aperture or create a new one
518 m_currentApertureIdx = GetOrCreateAperture( aSize, aRadius, aRotation, aType,
519 aApertureAttribute );
520 fprintf( m_outputFile, "D%d*\n", m_apertures[m_currentApertureIdx].m_DCode );
521 }
522}
523
524
525void GERBER_PLOTTER::selectAperture( const std::vector<VECTOR2I>& aCorners,
526 const EDA_ANGLE& aRotation, APERTURE::APERTURE_TYPE aType,
527 int aApertureAttribute )
528{
529 bool change = ( m_currentApertureIdx < 0 ) ||
530 ( m_apertures[m_currentApertureIdx].m_Type != aType ) ||
531 ( m_apertures[m_currentApertureIdx].m_Corners.size() != aCorners.size() ) ||
532 ( m_apertures[m_currentApertureIdx].m_Rotation != aRotation );
533
534 if( !change ) // Compare corner lists
535 {
536 for( size_t ii = 0; ii < aCorners.size(); ii++ )
537 {
538 if( aCorners[ii] != m_apertures[m_currentApertureIdx].m_Corners[ii] )
539 {
540 change = true;
541 break;
542 }
543 }
544 }
545
546 if( !change )
547 change = m_apertures[m_currentApertureIdx].m_ApertureAttribute != aApertureAttribute;
548
549 if( change )
550 {
551 // Pick an existing aperture or create a new one
552 m_currentApertureIdx = GetOrCreateAperture( aCorners, aRotation, aType,
553 aApertureAttribute );
554 fprintf( m_outputFile, "D%d*\n", m_apertures[m_currentApertureIdx].m_DCode );
555 }
556}
557
558
559void GERBER_PLOTTER::selectAperture( int aDiameter, const EDA_ANGLE& aPolygonRotation,
560 APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
561{
562 // Pick an existing aperture or create a new one, matching the
563 // aDiameter, aPolygonRotation, type and attributes for type =
564 // AT_REGULAR_POLY3 to AT_REGULAR_POLY12
565
568
569 VECTOR2I size( aDiameter, (int) ( aPolygonRotation.AsDegrees() * 1000.0 ) );
570 selectAperture( VECTOR2I( 0, 0 ), aDiameter / 2, aPolygonRotation, aType, aApertureAttribute );
571}
572
573
575{
576 wxASSERT( m_outputFile );
577
578 bool useX1StructuredComment = false;
579
580 if( !m_useX2format )
581 useX1StructuredComment = true;
582
583 // Init
584 for( APERTURE& tool : m_apertures )
585 {
586 // aperture sizes are in inch or mm, regardless the
587 // coordinates format
588 double fscale = 0.0001 * m_plotScale / m_IUsPerDecimil; // inches
589
590 if(! m_gerberUnitInch )
591 fscale *= 25.4; // size in mm
592
593 int attribute = tool.m_ApertureAttribute;
594
595 if( attribute != m_apertureAttribute )
596 {
599 useX1StructuredComment ).c_str(), m_outputFile );
600 }
601
602 fprintf( m_outputFile, "%%ADD%d", tool.m_DCode );
603
604 /* Please note: the Gerber specs for mass parameters say that
605 exponential syntax is *not* allowed and the decimal point should
606 also be always inserted. So the %g format is ruled out, but %f is fine
607 (the # modifier forces the decimal point). Sadly the %f formatter
608 can't remove trailing zeros but that's not a problem, since nothing
609 forbid it (the file is only slightly longer) */
610
611 switch( tool.m_Type )
612 {
614 fprintf( m_outputFile, "C,%#f*%%\n", tool.GetDiameter() * fscale );
615 break;
616
618 fprintf( m_outputFile, "R,%#fX%#f*%%\n", tool.m_Size.x * fscale,
619 tool.m_Size.y * fscale );
620 break;
621
623 fprintf( m_outputFile, "C,%#f*%%\n", tool.m_Size.x * fscale );
624 break;
625
627 fprintf( m_outputFile, "O,%#fX%#f*%%\n", tool.m_Size.x * fscale,
628 tool.m_Size.y * fscale );
629 break;
630
642 fprintf( m_outputFile, "P,%#fX%dX%#f*%%\n", tool.GetDiameter() * fscale,
643 tool.GetRegPolyVerticeCount(), tool.GetRotation().AsDegrees() );
644 break;
645
646 case APERTURE::AM_ROUND_RECT: // Aperture macro for round rect pads
647 {
648 // The aperture macro needs coordinates of the centers of the 4 corners
649 std::vector<VECTOR2I> corners;
650 VECTOR2I half_size( tool.m_Size.x/2-tool.m_Radius, tool.m_Size.y/2-tool.m_Radius );
651
652 // Ensure half_size.x and half_size.y > minimal value to avoid shapes
653 // with null size (especially the rectangle with coordinates corners)
654 // Because the minimal value for a non nul Gerber coord in 10nm
655 // in format 4.5, use 10 nm as minimal value.
656 // (Even in 4.6 format, use 10 nm, because gerber viewers can have
657 // a internal unit bigger than 1 nm)
658 const int min_size_value = 10;
659 half_size.x = std::max( half_size.x, min_size_value );
660 half_size.y = std::max( half_size.y, min_size_value );
661
662 corners.emplace_back( -half_size.x, -half_size.y );
663 corners.emplace_back( half_size.x, -half_size.y );
664 corners.emplace_back( half_size.x, half_size.y );
665 corners.emplace_back( -half_size.x, half_size.y );
666
667 // Rotate the corner coordinates:
668 for( int ii = 0; ii < 4; ii++ )
669 RotatePoint( corners[ii], -tool.m_Rotation );
670
671 fprintf( m_outputFile, "%s,%#fX", APER_MACRO_ROUNDRECT_NAME, tool.m_Radius * fscale );
672
673 // Add each corner
674 for( int ii = 0; ii < 4; ii++ )
675 {
676 fprintf( m_outputFile, "%#fX%#fX", corners[ii].x * fscale, corners[ii].y * fscale );
677 }
678
679 fprintf( m_outputFile, "0*%%\n" );
680 }
681 break;
682
683 case APERTURE::AM_ROT_RECT: // Aperture macro for rotated rect pads
684 fprintf( m_outputFile, "%s,%#fX%#fX%#f*%%\n", APER_MACRO_ROT_RECT_NAME,
685 tool.m_Size.x * fscale, tool.m_Size.y * fscale, tool.m_Rotation.AsDegrees() );
686 break;
687
688 case APERTURE::APER_MACRO_OUTLINE4P: // Aperture macro for trapezoid pads
689 case APERTURE::APER_MACRO_OUTLINE5P: // Aperture macro for chamfered rect pads
690 case APERTURE::APER_MACRO_OUTLINE6P: // Aperture macro for chamfered rect pads
691 case APERTURE::APER_MACRO_OUTLINE7P: // Aperture macro for chamfered rect pads
692 case APERTURE::APER_MACRO_OUTLINE8P: // Aperture macro for chamfered rect pads
693 switch( tool.m_Type )
694 {
696 fprintf( m_outputFile, "%s,", APER_MACRO_OUTLINE4P_NAME );
697 break;
699 fprintf( m_outputFile, "%s,", APER_MACRO_OUTLINE5P_NAME );
700 break;
702 fprintf( m_outputFile, "%s,", APER_MACRO_OUTLINE6P_NAME );
703 break;
705 fprintf( m_outputFile, "%s,", APER_MACRO_OUTLINE7P_NAME );
706 break;
708 fprintf( m_outputFile, "%s,", APER_MACRO_OUTLINE8P_NAME );
709 break;
710 default:
711 break;
712 }
713
714 // Output all corners (should be 4 to 8 corners)
715 // Remember: the Y coordinate must be negated, due to the fact in Pcbnew
716 // the Y axis is from top to bottom
717 for( size_t ii = 0; ii < tool.m_Corners.size(); ii++ )
718 {
719 fprintf( m_outputFile, "%#fX%#fX", tool.m_Corners[ii].x * fscale,
720 -tool.m_Corners[ii].y * fscale );
721 }
722
723 // close outline and output rotation
724 fprintf( m_outputFile, "%#f*%%\n", tool.m_Rotation.AsDegrees() );
725 break;
726
727 case APERTURE::AM_ROTATED_OVAL: // Aperture macro for rotated oval pads
728 // (not rotated is a primitive)
729 // m_Size.x = full length; m_Size.y = width, and the macro aperture expects
730 // the position of ends
731 {
732 // the seg_len is the distance between the 2 circle centers
733 int seg_len = tool.m_Size.x - tool.m_Size.y;
734
735 // Center of the circle on the segment start point:
736 VECTOR2I start( seg_len/2, 0 );
737
738 // Center of the circle on the segment end point:
739 VECTOR2I end( - seg_len/2, 0 );
740
741 RotatePoint( start, tool.m_Rotation );
742 RotatePoint( end, tool.m_Rotation );
743
744 fprintf( m_outputFile, "%s,%#fX%#fX%#fX%#fX%#fX0*%%\n", APER_MACRO_SHAPE_OVAL_NAME,
745 tool.m_Size.y * fscale, // width
746 start.x * fscale, -start.y * fscale, // X,Y corner start pos
747 end.x * fscale, -end.y * fscale ); // X,Y cornerend pos
748 }
749 break;
750
752 {
753 // Find the aperture macro name in the list of aperture macro
754 // created on the fly for this polygon:
755 int idx = m_am_freepoly_list.FindAm( tool.m_Corners );
756
757 // Write DCODE id ( "%ADDxx" is already in buffer) and rotation
758 // the full line is something like :%ADD12FreePoly1,45.000000*%
759 fprintf( m_outputFile, "%s%d,%#f*%%\n", AM_FREEPOLY_BASENAME, idx,
760 tool.m_Rotation.AsDegrees() );
761 break;
762 }
763 }
764
765 m_apertureAttribute = attribute;
766
767 // Currently reset the aperture attribute. Perhaps a better optimization
768 // is to store the last attribute
769 if( attribute )
770 {
771 if( m_useX2format )
772 fputs( "%TD*%\n", m_outputFile );
773 else
774 fputs( "G04 #@! TD*\n", m_outputFile );
775
777 }
778
779 }
780}
781
782
783void GERBER_PLOTTER::PenTo( const VECTOR2I& aPos, char plume )
784{
785 wxASSERT( m_outputFile );
786 VECTOR2D pos_dev = userToDeviceCoordinates( aPos );
787
788 switch( plume )
789 {
790 case 'Z':
791 break;
792
793 case 'U':
794 emitDcode( pos_dev, 2 );
795 break;
796
797 case 'D':
798 emitDcode( pos_dev, 1 );
799 }
800
801 m_penState = plume;
802}
803
804
805void GERBER_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width )
806{
807 std::vector<VECTOR2I> cornerList;
808
809 cornerList.reserve( 5 );
810
811 // Build corners list
812 cornerList.push_back( p1 );
813
814 VECTOR2I corner( p1.x, p2.y );
815 cornerList.push_back( corner );
816 cornerList.push_back( p2 );
817 corner.x = p2.x;
818 corner.y = p1.y;
819 cornerList.push_back( corner );
820 cornerList.push_back( p1 );
821
822 PlotPoly( cornerList, fill, width );
823}
824
825
826void GERBER_PLOTTER::Circle( const VECTOR2I& aCenter, int aDiameter, FILL_T aFill, int aWidth )
827{
828 Arc( aCenter, ANGLE_0, ANGLE_180, aDiameter / 2, aFill, aWidth );
829 Arc( aCenter, ANGLE_180, ANGLE_180, aDiameter / 2, aFill, aWidth );
830}
831
832
833
834void GERBER_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
835 const EDA_ANGLE& aAngle, double aRadius, FILL_T aFill, int aWidth )
836{
837 SetCurrentLineWidth( aWidth );
838
839 double arcLength = std::abs( aRadius * aAngle.AsRadians() );
840
841 if( arcLength < 100 || std::abs( aAngle.AsDegrees() ) < 0.1 )
842 {
843 // Prevent plotting very short arcs as full circles, especially with 4.5 mm precision.
844 // Also reduce the risk of integer overflow issues.
845 polyArc( aCenter, aStartAngle, aAngle, aRadius, aFill, aWidth );
846 }
847 else
848 {
849 EDA_ANGLE endAngle = aStartAngle + aAngle;
850
851 // aFill is not used here.
852 plotArc( aCenter, aStartAngle, endAngle, aRadius, false );
853 }
854}
855
856
857void GERBER_PLOTTER::plotArc( const SHAPE_ARC& aArc, bool aPlotInRegion )
858{
859 VECTOR2I start( aArc.GetP0() );
860 VECTOR2I end( aArc.GetP1() );
861 VECTOR2I center( aArc.GetCenter() );
862
863 if( !aPlotInRegion )
864 MoveTo( start);
865 else
866 LineTo( start );
867
868 VECTOR2D devEnd = userToDeviceCoordinates( end );
869
870 // devRelCenter is the position on arc center relative to the arc start, in Gerber coord.
871 // Warning: it is **not** userToDeviceCoordinates( center - start ) when the plotter
872 // has an offset.
873 VECTOR2D devRelCenter = userToDeviceCoordinates( center ) - userToDeviceCoordinates( start );
874
875 // We need to know if the arc is CW or CCW in device coordinates, so build this arc.
876 SHAPE_ARC deviceArc( userToDeviceCoordinates( start ),
878 devEnd, 0 );
879
880 fprintf( m_outputFile, "G75*\n" ); // Multiquadrant (360 degrees) mode
881
882 if( deviceArc.IsClockwise() )
883 fprintf( m_outputFile, "G02*\n" ); // Active circular interpolation, CW
884 else
885 fprintf( m_outputFile, "G03*\n" ); // Active circular interpolation, CCW
886
887 fprintf( m_outputFile, "X%dY%dI%dJ%dD01*\n",
888 KiROUND( devEnd.x ), KiROUND( devEnd.y ),
889 KiROUND( devRelCenter.x ), KiROUND( devRelCenter.y ) );
890
891 fprintf( m_outputFile, "G01*\n" ); // Back to linear interpolate (perhaps useless here).
892}
893
894
895void GERBER_PLOTTER::plotArc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle,
896 const EDA_ANGLE& aEndAngle, double aRadius, bool aPlotInRegion )
897{
898 VECTOR2I start, end;
899 start.x = KiROUND( aCenter.x + aRadius * aStartAngle.Cos() );
900 start.y = KiROUND( aCenter.y + aRadius * aStartAngle.Sin() );
901
902 if( !aPlotInRegion )
903 MoveTo( start );
904 else
905 LineTo( start );
906
907 end.x = KiROUND( aCenter.x + aRadius * aEndAngle.Cos() );
908 end.y = KiROUND( aCenter.y + aRadius * aEndAngle.Sin() );
909 VECTOR2D devEnd = userToDeviceCoordinates( end );
910
911 // devRelCenter is the position on arc center relative to the arc start, in Gerber coord.
912 VECTOR2D devRelCenter = userToDeviceCoordinates( aCenter ) - userToDeviceCoordinates( start );
913
914 fprintf( m_outputFile, "G75*\n" ); // Multiquadrant (360 degrees) mode
915
916 if( aStartAngle > aEndAngle )
917 fprintf( m_outputFile, "G03*\n" ); // Active circular interpolation, CCW
918 else
919 fprintf( m_outputFile, "G02*\n" ); // Active circular interpolation, CW
920
921 fprintf( m_outputFile, "X%dY%dI%dJ%dD01*\n",
922 KiROUND( devEnd.x ), KiROUND( devEnd.y ),
923 KiROUND( devRelCenter.x ), KiROUND( devRelCenter.y ) );
924
925 fprintf( m_outputFile, "G01*\n" ); // Back to linear interpolate (perhaps useless here).
926}
927
928
930{
931 if( aPoly.PointCount() <= 2 )
932 return;
933
934 bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
935
936 if( aGbrMetadata )
937 {
938 std::string attrib = aGbrMetadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
939
940 if( !attrib.empty() )
941 {
942 fputs( attrib.c_str(), m_outputFile );
943 clearTA_AperFunction = true;
944 }
945 }
946
947 PlotPoly( aPoly, FILL_T::FILLED_SHAPE, 0 , aGbrMetadata );
948
949 // Clear the TA attribute, to avoid the next item to inherit it:
950 if( clearTA_AperFunction )
951 {
952 if( m_useX2format )
953 {
954 fputs( "%TD.AperFunction*%\n", m_outputFile );
955 }
956 else
957 {
958 fputs( "G04 #@! TD.AperFunction*\n", m_outputFile );
959 }
960 }
961}
962
963
964void GERBER_PLOTTER::PlotGerberRegion( const std::vector<VECTOR2I>& aCornerList,
965 GBR_METADATA* aGbrMetadata )
966{
967 if( aCornerList.size() <= 2 )
968 return;
969
970 bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
971
972 if( aGbrMetadata )
973 {
974 std::string attrib = aGbrMetadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
975
976 if( !attrib.empty() )
977 {
978 fputs( attrib.c_str(), m_outputFile );
979 clearTA_AperFunction = true;
980 }
981 }
982
983 PlotPoly( aCornerList, FILL_T::FILLED_SHAPE, 0, aGbrMetadata );
984
985 // Clear the TA attribute, to avoid the next item to inherit it:
986 if( clearTA_AperFunction )
987 {
988 if( m_useX2format )
989 {
990 fputs( "%TD.AperFunction*%\n", m_outputFile );
991 }
992 else
993 {
994 fputs( "G04 #@! TD.AperFunction*\n", m_outputFile );
995 }
996 }
997}
998
999
1001 int aWidth, GBR_METADATA* aGbrMetadata )
1002{
1003 // plot a filled polygon using Gerber region, therefore adding X2 attributes
1004 // to the solid polygon
1005 if( aWidth || aFill == FILL_T::NO_FILL )
1006 PlotPoly( aPoly, FILL_T::NO_FILL, aWidth, aGbrMetadata );
1007
1008 if( aFill != FILL_T::NO_FILL )
1009 PlotGerberRegion( aPoly, aGbrMetadata );
1010}
1011
1012
1013void GERBER_PLOTTER::PlotPoly( const SHAPE_LINE_CHAIN& aPoly, FILL_T aFill, int aWidth,
1014 void* aData )
1015{
1016 if( aPoly.CPoints().size() <= 1 )
1017 return;
1018
1019 // Gerber format does not know filled polygons with thick outline
1020 // Therefore, to plot a filled polygon with outline having a thickness,
1021 // one should plot outline as thick segments
1022 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1023
1024 if( gbr_metadata )
1025 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1026
1027 if( aFill != FILL_T::NO_FILL )
1028 {
1029 fputs( "G36*\n", m_outputFile );
1030
1031 MoveTo( VECTOR2I( aPoly.CPoint( 0 ) ) );
1032
1033 fputs( "G01*\n", m_outputFile ); // Set linear interpolation.
1034
1035 for( int ii = 1; ii < aPoly.PointCount(); ii++ )
1036 {
1037 int arcindex = aPoly.ArcIndex( ii );
1038
1039 if( arcindex < 0 )
1040 {
1042 LineTo( VECTOR2I( aPoly.CPoint( ii ) ) );
1043 }
1044 else
1045 {
1046 const SHAPE_ARC& arc = aPoly.Arc( arcindex );
1047
1048 plotArc( arc, true );
1049
1050 // skip points on arcs, since we plot the arc itself
1051 while( ii+1 < aPoly.PointCount() && arcindex == aPoly.ArcIndex( ii+1 ) )
1052 ii++;
1053 }
1054 }
1055
1056 // If the polygon is not closed, close it:
1057 if( aPoly.CPoint( 0 ) != aPoly.CPoint( -1 ) )
1058 FinishTo( VECTOR2I( aPoly.CPoint( 0 ) ) );
1059
1060 fputs( "G37*\n", m_outputFile );
1061 }
1062
1063 if( aWidth > 0 || aFill == FILL_T::NO_FILL ) // Draw the polyline/polygon outline
1064 {
1065 SetCurrentLineWidth( aWidth, gbr_metadata );
1066
1067 MoveTo( VECTOR2I( aPoly.CPoint( 0 ) ) );
1068
1069 for( int ii = 1; ii < aPoly.PointCount(); ii++ )
1070 {
1071 int arcindex = aPoly.ArcIndex( ii );
1072
1073 if( arcindex < 0 )
1074 {
1076 LineTo( VECTOR2I( aPoly.CPoint( ii ) ) );
1077 }
1078 else
1079 {
1080 const SHAPE_ARC& arc = aPoly.Arc( arcindex );
1081
1082 plotArc( arc, true );
1083
1084 // skip points on arcs, since we plot the arc itself
1085 while( ii+1 < aPoly.PointCount() && arcindex == aPoly.ArcIndex( ii+1 ) )
1086 ii++;
1087 }
1088 }
1089
1090 // Ensure the thick outline is closed for filled polygons
1091 // (if not filled, could be only a polyline)
1092 if( ( aPoly.CPoint( 0 ) != aPoly.CPoint( -1 ) )
1093 && ( aPoly.IsClosed() || aFill != FILL_T::NO_FILL ) )
1094 LineTo( VECTOR2I( aPoly.CPoint( 0 ) ) );
1095
1096 PenFinish();
1097 }
1098}
1099
1100
1101void GERBER_PLOTTER::PlotPoly( const std::vector<VECTOR2I>& aCornerList, FILL_T aFill, int aWidth,
1102 void * aData )
1103{
1104 if( aCornerList.size() <= 1 )
1105 return;
1106
1107 // Gerber format does not know filled polygons with thick outline
1108 // Therefore, to plot a filled polygon with outline having a thickness,
1109 // one should plot outline as thick segments
1110 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1111
1112 if( gbr_metadata )
1113 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1114
1115 if( aFill != FILL_T::NO_FILL )
1116 {
1117 fputs( "G36*\n", m_outputFile );
1118
1119 MoveTo( aCornerList[0] );
1120 fputs( "G01*\n", m_outputFile ); // Set linear interpolation.
1121
1122 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1123 LineTo( aCornerList[ii] );
1124
1125 // If the polygon is not closed, close it:
1126 if( aCornerList[0] != aCornerList[aCornerList.size()-1] )
1127 FinishTo( aCornerList[0] );
1128
1129 fputs( "G37*\n", m_outputFile );
1130 }
1131
1132 if( aWidth > 0 || aFill == FILL_T::NO_FILL ) // Draw the polyline/polygon outline
1133 {
1134 SetCurrentLineWidth( aWidth, gbr_metadata );
1135
1136 MoveTo( aCornerList[0] );
1137
1138 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1139 LineTo( aCornerList[ii] );
1140
1141 // Ensure the thick outline is closed for filled polygons
1142 // (if not filled, could be only a polyline)
1143 if( aFill != FILL_T::NO_FILL && ( aCornerList[aCornerList.size() - 1] != aCornerList[0] ) )
1144 LineTo( aCornerList[0] );
1145
1146 PenFinish();
1147 }
1148}
1149
1150
1151void GERBER_PLOTTER::ThickSegment( const VECTOR2I& start, const VECTOR2I& end, int width,
1152 OUTLINE_MODE tracemode, void* aData )
1153{
1154 if( tracemode == FILLED )
1155 {
1156 GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1157 SetCurrentLineWidth( width, gbr_metadata );
1158
1159 if( gbr_metadata )
1160 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1161
1162 MoveTo( start );
1163 FinishTo( end );
1164 }
1165 else
1166 {
1168 segmentAsOval( start, end, width, tracemode );
1169 }
1170}
1171
1172
1173void GERBER_PLOTTER::ThickArc( const VECTOR2D& aCentre, const EDA_ANGLE& aStartAngle,
1174 const EDA_ANGLE& aAngle, double aRadius, int aWidth,
1175 OUTLINE_MODE aTraceMode, void* aData )
1176{
1177 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1178 SetCurrentLineWidth( aWidth, gbr_metadata );
1179
1180 if( gbr_metadata )
1181 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1182
1183 if( aTraceMode == FILLED )
1184 {
1185 Arc( aCentre, aStartAngle, aAngle, aRadius, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1186 }
1187 else
1188 {
1190 Arc( aCentre, aStartAngle, aAngle, aRadius - ( aWidth - m_currentPenWidth ) / 2,
1191 FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1192 Arc( aCentre, aStartAngle, aAngle, aRadius + ( aWidth - m_currentPenWidth ) / 2,
1193 FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1194 }
1195}
1196
1197
1198void GERBER_PLOTTER::ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width,
1199 OUTLINE_MODE tracemode, void* aData )
1200{
1201 GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1202 SetCurrentLineWidth( width, gbr_metadata );
1203
1204 if( gbr_metadata )
1205 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1206
1207 if( tracemode == FILLED )
1208 {
1209 Rect( p1, p2, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1210 }
1211 else
1212 {
1214 VECTOR2I offsetp1( p1.x - ( width - m_currentPenWidth ) / 2,
1215 p1.y - (width - m_currentPenWidth) / 2 );
1216 VECTOR2I offsetp2( p2.x + ( width - m_currentPenWidth ) / 2,
1217 p2.y + (width - m_currentPenWidth) / 2 );
1218 Rect( offsetp1, offsetp2, FILL_T::NO_FILL, -1 );
1219 offsetp1.x += (width - m_currentPenWidth);
1220 offsetp1.y += (width - m_currentPenWidth);
1221 offsetp2.x -= (width - m_currentPenWidth);
1222 offsetp2.y -= (width - m_currentPenWidth);
1223 Rect( offsetp1, offsetp2, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1224 }
1225}
1226
1227
1228void GERBER_PLOTTER::ThickCircle( const VECTOR2I& pos, int diametre, int width,
1229 OUTLINE_MODE tracemode, void* aData )
1230{
1231 GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1232 SetCurrentLineWidth( width, gbr_metadata );
1233
1234 if( gbr_metadata )
1235 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1236
1237 if( tracemode == FILLED )
1238 {
1239 Circle( pos, diametre, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1240 }
1241 else
1242 {
1244 Circle( pos, diametre - (width - m_currentPenWidth), FILL_T::NO_FILL,
1246 Circle( pos, diametre + (width - m_currentPenWidth), FILL_T::NO_FILL,
1248 }
1249}
1250
1251
1252void GERBER_PLOTTER::FilledCircle( const VECTOR2I& pos, int diametre,
1253 OUTLINE_MODE tracemode, void* aData )
1254{
1255 // A filled circle is a graphic item, not a pad.
1256 // So it is drawn, not flashed.
1257 GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1258
1259 if( gbr_metadata )
1260 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1261
1262 if( tracemode == FILLED )
1263 {
1264 // Draw a circle of diameter = diameter/2 with a line thickness = radius,
1265 // To create a filled circle
1266 SetCurrentLineWidth( diametre/2, gbr_metadata );
1267 Circle( pos, diametre/2, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1268 }
1269 else
1270 {
1272 Circle( pos, diametre, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1273 }
1274}
1275
1276
1277void GERBER_PLOTTER::FlashPadCircle( const VECTOR2I& pos, int diametre, OUTLINE_MODE trace_mode,
1278 void* aData )
1279{
1280 VECTOR2I size( diametre, diametre );
1281 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1282
1283 if( trace_mode == SKETCH )
1284 {
1285 if( gbr_metadata )
1286 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1287
1289
1290 Circle( pos, diametre, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1291 }
1292 else
1293 {
1294 VECTOR2D pos_dev = userToDeviceCoordinates( pos );
1295
1296 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1297 selectAperture( size, 0, ANGLE_0, APERTURE::AT_CIRCLE, aperture_attrib );
1298
1299 if( gbr_metadata )
1300 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1301
1302 emitDcode( pos_dev, 3 );
1303 }
1304}
1305
1306
1307void GERBER_PLOTTER::FlashPadOval( const VECTOR2I& aPos, const VECTOR2I& aSize,
1308 const EDA_ANGLE& aOrient, OUTLINE_MODE aTraceMode, void* aData )
1309{
1310 wxASSERT( m_outputFile );
1311
1312 VECTOR2I size( aSize );
1313 EDA_ANGLE orient( aOrient );
1314 orient.Normalize();
1315 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1316
1317 // Flash a vertical or horizontal shape (this is a basic aperture).
1318 if( orient.IsCardinal() && aTraceMode == FILLED )
1319 {
1320 if( orient.IsCardinal90() )
1321 std::swap( size.x, size.y );
1322
1323 VECTOR2D pos_device = userToDeviceCoordinates( aPos );
1324 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1325 selectAperture( size, 0, ANGLE_0, APERTURE::AT_OVAL, aperture_attrib );
1326
1327 if( gbr_metadata )
1328 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1329
1330 emitDcode( pos_device, 3 );
1331 }
1332 else // Plot pad as region.
1333 // Only regions and flashed items accept a object attribute TO.P for the pin name
1334 {
1335 if( aTraceMode == FILLED )
1336 {
1337 #ifdef GBR_USE_MACROS_FOR_ROTATED_OVAL
1339 #endif
1340 {
1341 m_hasApertureRotOval = true;
1342 // We are using a aperture macro that expect size.y < size.x
1343 // i.e draw a horizontal line for rotation = 0.0
1344 // size.x = length, size.y = width
1345 if( size.x < size.y )
1346 {
1347 std::swap( size.x, size.y );
1348 orient += ANGLE_90;
1349
1350 if( orient > ANGLE_180 )
1351 orient -= ANGLE_180;
1352 }
1353
1354 VECTOR2D pos_device = userToDeviceCoordinates( aPos );
1355 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1356 selectAperture( size, 0, orient, APERTURE::AM_ROTATED_OVAL, aperture_attrib );
1357
1358 if( gbr_metadata )
1359 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1360
1361 emitDcode( pos_device, 3 );
1362 return;
1363 }
1364
1365 // Draw the oval as round rect pad with a radius = 50% min size)
1366 // In gerber file, it will be drawn as a region with arcs, and can be
1367 // detected as pads (similar to a flashed pad)
1368 FlashPadRoundRect( aPos, aSize, std::min( aSize.x, aSize.y ) / 2, orient, FILLED,
1369 aData );
1370 }
1371 else // Non filled shape: plot outlines:
1372 {
1373 if( size.x > size.y )
1374 {
1375 std::swap( size.x, size.y );
1376
1377 if( orient < ANGLE_270 )
1378 orient += ANGLE_90;
1379 else
1380 orient -= ANGLE_270;
1381 }
1382
1383 sketchOval( aPos, size, orient, -1 );
1384 }
1385 }
1386}
1387
1388
1389void GERBER_PLOTTER::FlashPadRect( const VECTOR2I& pos, const VECTOR2I& aSize,
1390 const EDA_ANGLE& aOrient, OUTLINE_MODE aTraceMode, void* aData )
1391
1392{
1393 wxASSERT( m_outputFile );
1394
1395 VECTOR2I size( aSize );
1396 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1397
1398 // Horizontal / vertical rect can use a basic aperture (not a macro)
1399 // so use it for rotation n*90 deg
1400 if( aOrient.IsCardinal() )
1401 {
1402 // Build the not rotated equivalent shape:
1403 if( aOrient.IsCardinal90() )
1404 std::swap( size.x, size.y );
1405
1406 if( aTraceMode == SKETCH )
1407 {
1408 if( gbr_metadata )
1409 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1410
1412 Rect( VECTOR2I( pos.x - ( size.x / 2 ), pos.y - (size.y / 2 ) ),
1413 VECTOR2I( pos.x + ( size.x / 2 ), pos.y + (size.y / 2 ) ),
1414 FILL_T::NO_FILL, GetCurrentLineWidth() );
1415 }
1416 else
1417 {
1418 VECTOR2D pos_device = userToDeviceCoordinates( pos );
1419 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1420 selectAperture( size, 0, ANGLE_0, APERTURE::AT_RECT, aperture_attrib );
1421
1422 if( gbr_metadata )
1423 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1424
1425 emitDcode( pos_device, 3 );
1426 }
1427 }
1428 else
1429 {
1430 #ifdef GBR_USE_MACROS_FOR_ROTATED_RECT
1431 if( aTraceMode != SKETCH && !m_gerberDisableApertMacros )
1432 {
1433 m_hasApertureRotRect = true;
1434
1435 VECTOR2D pos_device = userToDeviceCoordinates( pos );
1436 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1437 selectAperture( size, 0, aOrient, APERTURE::AM_ROT_RECT, aperture_attrib );
1438
1439 if( gbr_metadata )
1440 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1441
1442 emitDcode( pos_device, 3 );
1443 }
1444 else
1445 #endif
1446 {
1447 // plot pad shape as Gerber region
1448 VECTOR2I coord[4];
1449
1450 // coord[0] is assumed the lower left
1451 // coord[1] is assumed the upper left
1452 // coord[2] is assumed the upper right
1453 // coord[3] is assumed the lower right
1454 coord[0].x = -size.x/2; // lower left
1455 coord[0].y = size.y/2;
1456 coord[1].x = -size.x/2; // upper left
1457 coord[1].y = -size.y/2;
1458 coord[2].x = size.x/2; // upper right
1459 coord[2].y = -size.y/2;
1460 coord[3].x = size.x/2; // lower right
1461 coord[3].y = size.y/2;
1462
1463 FlashPadTrapez( pos, coord, aOrient, aTraceMode, aData );
1464 }
1465 }
1466}
1467
1468
1469void GERBER_PLOTTER::FlashPadRoundRect( const VECTOR2I& aPadPos, const VECTOR2I& aSize,
1470 int aCornerRadius, const EDA_ANGLE& aOrient,
1471 OUTLINE_MODE aTraceMode, void* aData )
1472
1473{
1474 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1475
1476 if( aTraceMode != FILLED )
1477 {
1478 SHAPE_POLY_SET outline;
1479 TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient, aCornerRadius, 0.0,
1481
1483
1484 std::vector<VECTOR2I> cornerList;
1485
1486 // TransformRoundRectToPolygon creates only one convex polygon
1487 SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
1488 cornerList.reserve( poly.PointCount() + 1 );
1489
1490 for( int ii = 0; ii < poly.PointCount(); ++ii )
1491 cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
1492
1493 // Close polygon
1494 cornerList.push_back( cornerList[0] );
1495
1496 // plot outlines
1497 PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), gbr_metadata );
1498 }
1499 else
1500 {
1501 #ifdef GBR_USE_MACROS_FOR_ROUNDRECT
1503 #endif
1504 {
1506
1507 VECTOR2D pos_dev = userToDeviceCoordinates( aPadPos );
1508 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1509 selectAperture( aSize, aCornerRadius, aOrient, APERTURE::AM_ROUND_RECT,
1510 aperture_attrib );
1511
1512 if( gbr_metadata )
1513 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1514
1515 emitDcode( pos_dev, 3 );
1516 return;
1517 }
1518
1519 // A Pad RoundRect is plotted as a Gerber region.
1520 // Initialize region metadata:
1521 bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
1522
1523 if( gbr_metadata )
1524 {
1525 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1526 std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
1527
1528 if( !attrib.empty() )
1529 {
1530 fputs( attrib.c_str(), m_outputFile );
1531 clearTA_AperFunction = true;
1532 }
1533 }
1534
1535 // Plot the region using arcs in corners.
1536 plotRoundRectAsRegion( aPadPos, aSize, aCornerRadius, aOrient );
1537
1538 // Clear the TA attribute, to avoid the next item to inherit it:
1539 if( clearTA_AperFunction )
1540 {
1541 if( m_useX2format )
1542 fputs( "%TD.AperFunction*%\n", m_outputFile );
1543 else
1544 fputs( "G04 #@! TD.AperFunction*\n", m_outputFile );
1545 }
1546 }
1547}
1548
1549
1550void GERBER_PLOTTER::plotRoundRectAsRegion( const VECTOR2I& aRectCenter, const VECTOR2I& aSize,
1551 int aCornerRadius, const EDA_ANGLE& aOrient )
1552{
1553 // The region outline is generated by 4 sides and 4 90 deg arcs
1554 // 1 --- 2
1555 // | c |
1556 // 4 --- 3
1557
1558 // Note also in user coordinates the Y axis is from top to bottom
1559 // for historical reasons.
1560
1561 // A helper structure to handle outlines coordinates (segments and arcs)
1562 // in user coordinates
1563 struct RR_EDGE
1564 {
1565 VECTOR2I m_start;
1566 VECTOR2I m_end;
1567 VECTOR2I m_center;
1568 EDA_ANGLE m_arc_angle_start;
1569 };
1570
1571 int hsizeX = aSize.x/2;
1572 int hsizeY = aSize.y/2;
1573
1574 RR_EDGE curr_edge;
1575 std::vector<RR_EDGE> rr_outline;
1576
1577 rr_outline.reserve( 4 );
1578
1579 // Build outline coordinates, relative to rectangle center, rotation 0:
1580
1581 // Top left corner 1 (and 4 to 1 left vertical side @ x=-hsizeX)
1582 curr_edge.m_start.x = -hsizeX;
1583 curr_edge.m_start.y = hsizeY - aCornerRadius;
1584 curr_edge.m_end.x = curr_edge.m_start.x;
1585 curr_edge.m_end.y = -hsizeY + aCornerRadius;
1586 curr_edge.m_center.x = -hsizeX + aCornerRadius;
1587 curr_edge.m_center.y = curr_edge.m_end.y;
1588 curr_edge.m_arc_angle_start = aOrient + ANGLE_180;
1589
1590 rr_outline.push_back( curr_edge );
1591
1592 // Top right corner 2 (and 1 to 2 top horizontal side @ y=-hsizeY)
1593 curr_edge.m_start.x = -hsizeX + aCornerRadius;
1594 curr_edge.m_start.y = -hsizeY;
1595 curr_edge.m_end.x = hsizeX - aCornerRadius;
1596 curr_edge.m_end.y = curr_edge.m_start.y;
1597 curr_edge.m_center.x = curr_edge.m_end.x;
1598 curr_edge.m_center.y = -hsizeY + aCornerRadius;
1599 curr_edge.m_arc_angle_start = aOrient + ANGLE_90;
1600
1601 rr_outline.push_back( curr_edge );
1602
1603 // bottom right corner 3 (and 2 to 3 right vertical side @ x=hsizeX)
1604 curr_edge.m_start.x = hsizeX;
1605 curr_edge.m_start.y = -hsizeY + aCornerRadius;
1606 curr_edge.m_end.x = curr_edge.m_start.x;
1607 curr_edge.m_end.y = hsizeY - aCornerRadius;
1608 curr_edge.m_center.x = hsizeX - aCornerRadius;
1609 curr_edge.m_center.y = curr_edge.m_end.y;
1610 curr_edge.m_arc_angle_start = aOrient + ANGLE_0;
1611
1612 rr_outline.push_back( curr_edge );
1613
1614 // bottom left corner 4 (and 3 to 4 bottom horizontal side @ y=hsizeY)
1615 curr_edge.m_start.x = hsizeX - aCornerRadius;
1616 curr_edge.m_start.y = hsizeY;
1617 curr_edge.m_end.x = -hsizeX + aCornerRadius;
1618 curr_edge.m_end.y = curr_edge.m_start.y;
1619 curr_edge.m_center.x = curr_edge.m_end.x;
1620 curr_edge.m_center.y = hsizeY - aCornerRadius;
1621 curr_edge.m_arc_angle_start = aOrient - ANGLE_90;
1622
1623 rr_outline.push_back( curr_edge );
1624
1625 // Move relative coordinates to the actual location and rotation:
1626 VECTOR2I arc_last_center;
1627 EDA_ANGLE arc_last_angle = curr_edge.m_arc_angle_start - ANGLE_90;
1628
1629 for( RR_EDGE& rr_edge: rr_outline )
1630 {
1631 RotatePoint( rr_edge.m_start, aOrient );
1632 RotatePoint( rr_edge.m_end, aOrient );
1633 RotatePoint( rr_edge.m_center, aOrient );
1634 rr_edge.m_start += aRectCenter;
1635 rr_edge.m_end += aRectCenter;
1636 rr_edge.m_center += aRectCenter;
1637 arc_last_center = rr_edge.m_center;
1638 }
1639
1640 // Ensure the region is a closed polygon, i.e. the end point of last segment
1641 // (end of arc) is the same as the first point. Rounding issues can create a
1642 // small difference, mainly for rotated pads.
1643 // calculate last point (end of last arc):
1644 VECTOR2I last_pt;
1645 last_pt.x = arc_last_center.x + KiROUND( aCornerRadius * arc_last_angle.Cos() );
1646 last_pt.y = arc_last_center.y - KiROUND( aCornerRadius * arc_last_angle.Sin() );
1647
1648 VECTOR2I first_pt = rr_outline[0].m_start;
1649
1650#if 0 // For test only:
1651 if( last_pt != first_pt )
1652 wxLogMessage( wxS( "first pt %d %d last pt %d %d" ),
1653 first_pt.x, first_pt.y, last_pt.x, last_pt.y );
1654#endif
1655
1656 fputs( "G36*\n", m_outputFile ); // Start region
1657 fputs( "G01*\n", m_outputFile ); // Set linear interpolation.
1658 first_pt = last_pt;
1659 MoveTo( first_pt ); // Start point of region, must be same as end point
1660
1661 for( RR_EDGE& rr_edge: rr_outline )
1662 {
1663 if( aCornerRadius ) // Guard: ensure we do not create arcs with radius = 0
1664 {
1665 // LineTo( rr_edge.m_end ); // made in plotArc()
1666 plotArc( rr_edge.m_center, -rr_edge.m_arc_angle_start,
1667 -rr_edge.m_arc_angle_start + ANGLE_90, aCornerRadius, true );
1668 }
1669 else
1670 {
1671 LineTo( rr_edge.m_end );
1672 }
1673 }
1674
1675 fputs( "G37*\n", m_outputFile ); // Close region
1676}
1677
1678
1679void GERBER_PLOTTER::FlashPadCustom( const VECTOR2I& aPadPos, const VECTOR2I& aSize,
1680 const EDA_ANGLE& aOrient, SHAPE_POLY_SET* aPolygons,
1681 OUTLINE_MODE aTraceMode, void* aData )
1682
1683{
1684 // A Pad custom is plotted as polygon (a region in Gerber language).
1685 GBR_METADATA gbr_metadata;
1686
1687 if( aData )
1688 gbr_metadata = *static_cast<GBR_METADATA*>( aData );
1689
1690 SHAPE_POLY_SET polyshape = aPolygons->CloneDropTriangulation();
1691
1692 if( aTraceMode != FILLED )
1693 {
1695 }
1696
1697 std::vector<VECTOR2I> cornerList;
1698
1699 for( int cnt = 0; cnt < polyshape.OutlineCount(); ++cnt )
1700 {
1701 SHAPE_LINE_CHAIN& poly = polyshape.Outline( cnt );
1702
1703 cornerList.clear();
1704
1705 for( int ii = 0; ii < poly.PointCount(); ++ii )
1706 cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
1707
1708 // Close polygon
1709 cornerList.push_back( cornerList[0] );
1710
1711 if( aTraceMode == SKETCH )
1712 {
1713 PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
1714 }
1715 else
1716 {
1717#ifdef GBR_USE_MACROS_FOR_CUSTOM_PAD
1719 || cornerList.size() > GBR_MACRO_FOR_CUSTOM_PAD_MAX_CORNER_COUNT )
1720 {
1721 PlotGerberRegion( cornerList, &gbr_metadata );
1722 }
1723 else
1724 {
1725 // An AM will be created. the shape must be in position 0,0 and orientation 0
1726 // to be able to reuse the same AM for pads having the same shape
1727 for( size_t ii = 0; ii < cornerList.size(); ii++ )
1728 {
1729 cornerList[ii] -= aPadPos;
1730 RotatePoint( cornerList[ii], -aOrient );
1731 }
1732
1733 VECTOR2D pos_dev = userToDeviceCoordinates( aPadPos );
1734 selectAperture( cornerList, aOrient, APERTURE::AM_FREE_POLYGON,
1735 gbr_metadata.GetApertureAttrib() );
1736 formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1737
1738 emitDcode( pos_dev, 3 );
1739 }
1740#else
1741 PlotGerberRegion( cornerList, &gbr_metadata );
1742#endif
1743 }
1744 }
1745}
1746
1747
1748void GERBER_PLOTTER::FlashPadChamferRoundRect( const VECTOR2I& aShapePos, const VECTOR2I& aPadSize,
1749 int aCornerRadius, double aChamferRatio,
1750 int aChamferPositions, const EDA_ANGLE& aPadOrient,
1751 OUTLINE_MODE aPlotMode, void* aData )
1752
1753{
1754 GBR_METADATA gbr_metadata;
1755
1756 if( aData )
1757 gbr_metadata = *static_cast<GBR_METADATA*>( aData );
1758
1759 VECTOR2D pos_device = userToDeviceCoordinates( aShapePos );
1760 SHAPE_POLY_SET outline;
1761 std::vector<VECTOR2I> cornerList;
1762
1763 bool hasRoundedCorner = aCornerRadius != 0 && aChamferPositions != 15;
1764
1765#ifdef GBR_USE_MACROS_FOR_CHAMFERED_RECT
1766 // Sketch mode or round rect shape or Apert Macros disabled
1767 if( aPlotMode != FILLED || hasRoundedCorner || m_gerberDisableApertMacros )
1768#endif
1769 {
1770 TransformRoundChamferedRectToPolygon( outline, aShapePos, aPadSize, aPadOrient,
1771 aCornerRadius, aChamferRatio, aChamferPositions, 0,
1773
1774 // Build the corner list
1775 const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
1776
1777 for( int ii = 0; ii < corners.PointCount(); ii++ )
1778 cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
1779
1780 // Close the polygon
1781 cornerList.push_back( cornerList[0] );
1782
1783 if( aPlotMode == SKETCH )
1784 {
1785 PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
1786 }
1787 else
1788 {
1789#ifdef GBR_USE_MACROS_FOR_CHAMFERED_ROUND_RECT
1791 {
1792 PlotGerberRegion( cornerList, &gbr_metadata );
1793 }
1794 else
1795 {
1796 // An AM will be created. the shape must be in position 0,0 and orientation 0
1797 // to be able to reuse the same AM for pads having the same shape
1798 for( size_t ii = 0; ii < cornerList.size(); ii++ )
1799 {
1800 cornerList[ii] -= aShapePos;
1801 RotatePoint( cornerList[ii], -aPadOrient );
1802 }
1803
1804 selectAperture( cornerList, aPadOrient, APERTURE::AM_FREE_POLYGON,
1805 gbr_metadata.GetApertureAttrib() );
1806 formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1807
1808 emitDcode( pos_device, 3 );
1809 }
1810#else
1811 PlotGerberRegion( cornerList, &gbr_metadata );
1812#endif
1813 }
1814
1815 return;
1816 }
1817
1818 // Build the chamfered polygon (4 to 8 corners )
1819 TransformRoundChamferedRectToPolygon( outline, VECTOR2I( 0, 0 ), aPadSize, ANGLE_0, 0,
1820 aChamferRatio, aChamferPositions, 0,
1822
1823 // Build the corner list
1824 const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
1825
1826 // Generate the polygon (4 to 8 corners )
1827 for( int ii = 0; ii < corners.PointCount(); ii++ )
1828 cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
1829
1830 switch( cornerList.size() )
1831 {
1832 case 4:
1834 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE4P,
1835 gbr_metadata.GetApertureAttrib() );
1836 break;
1837
1838 case 5:
1840 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE5P,
1841 gbr_metadata.GetApertureAttrib() );
1842 break;
1843
1844 case 6:
1846 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE6P,
1847 gbr_metadata.GetApertureAttrib() );
1848 break;
1849
1850 case 7:
1852 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE7P,
1853 gbr_metadata.GetApertureAttrib() );
1854 break;
1855
1856 case 8:
1858 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE8P,
1859 gbr_metadata.GetApertureAttrib() );
1860 break;
1861
1862 default:
1863 wxLogMessage( wxS( "FlashPadChamferRoundRect(): Unexpected number of corners (%d)" ),
1864 (int)cornerList.size() );
1865 break;
1866 }
1867
1868 formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1869
1870 emitDcode( pos_device, 3 );
1871}
1872
1873
1874void GERBER_PLOTTER::FlashPadTrapez( const VECTOR2I& aPadPos, const VECTOR2I* aCorners,
1875 const EDA_ANGLE& aPadOrient, OUTLINE_MODE aTraceMode,
1876 void* aData )
1877
1878{
1879 // polygon corners list
1880 std::vector<VECTOR2I> cornerList = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
1881
1882 // Draw the polygon and fill the interior as required
1883 for( unsigned ii = 0; ii < 4; ii++ )
1884 {
1885 RotatePoint( cornerList[ii], aPadOrient );
1886 cornerList[ii] += aPadPos;
1887 }
1888
1889 // Close the polygon
1890 cornerList.push_back( cornerList[0] );
1891
1892 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1893 GBR_METADATA metadata;
1894
1895 if( gbr_metadata )
1896 metadata = *gbr_metadata;
1897
1898 if( aTraceMode == SKETCH )
1899 {
1900 PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), &metadata );
1901 return;
1902 }
1903
1904 // Plot a filled polygon:
1905 #ifdef GBR_USE_MACROS_FOR_TRAPEZOID
1907 #endif
1908 {
1910 VECTOR2D pos_dev = userToDeviceCoordinates( aPadPos );
1911
1912 // polygon corners list
1913 std::vector<VECTOR2I> corners = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
1914 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1915 selectAperture( corners, aPadOrient, APERTURE::APER_MACRO_OUTLINE4P, aperture_attrib );
1916
1917 if( gbr_metadata )
1918 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1919
1920 emitDcode( pos_dev, 3 );
1921 return;
1922 }
1923
1924 PlotGerberRegion( cornerList, &metadata );
1925}
1926
1927
1928void GERBER_PLOTTER::FlashRegularPolygon( const VECTOR2I& aShapePos, int aDiameter,
1929 int aCornerCount, const EDA_ANGLE& aOrient,
1930 OUTLINE_MODE aTraceMode, void* aData )
1931{
1932 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1933
1934 GBR_METADATA metadata;
1935
1936 if( gbr_metadata )
1937 metadata = *gbr_metadata;
1938
1939 if( aTraceMode == SKETCH )
1940 {
1941 // Build the polygon:
1942 std::vector<VECTOR2I> cornerList;
1943
1944 EDA_ANGLE angle_delta = ANGLE_360 / aCornerCount;
1945
1946 for( int ii = 0; ii < aCornerCount; ii++ )
1947 {
1948 EDA_ANGLE rot = aOrient + ( angle_delta * ii );
1949 VECTOR2I vertice( aDiameter / 2, 0 );
1950
1951 RotatePoint( vertice, rot );
1952 vertice += aShapePos;
1953 cornerList.push_back( vertice );
1954 }
1955
1956 cornerList.push_back( cornerList[0] ); // Close the shape
1957
1958 PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
1959 }
1960 else
1961 {
1962 VECTOR2D pos_dev = userToDeviceCoordinates( aShapePos );
1963 int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1964
1965 APERTURE::APERTURE_TYPE apert_type =
1967 selectAperture( aDiameter, aOrient, apert_type, aperture_attrib );
1968
1969 if( gbr_metadata )
1970 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1971
1972 emitDcode( pos_dev, 3 );
1973 }
1974}
1975
1976
1978 const COLOR4D& aColor,
1979 const wxString& aText,
1980 const EDA_ANGLE& aOrient,
1981 const VECTOR2I& aSize,
1982 enum GR_TEXT_H_ALIGN_T aH_justify,
1983 enum GR_TEXT_V_ALIGN_T aV_justify,
1984 int aWidth,
1985 bool aItalic,
1986 bool aBold,
1987 bool aMultilineAllowed,
1988 KIFONT::FONT* aFont,
1989 const KIFONT::METRICS& aFontMetrics,
1990 void* aData )
1991{
1992 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1993
1994 if( gbr_metadata )
1995 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1996
1997 PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth,
1998 aItalic, aBold, aMultilineAllowed, aFont, aFontMetrics, aData );
1999}
2000
2001
2003 const COLOR4D& aColor,
2004 const wxString& aText,
2005 const TEXT_ATTRIBUTES& aAttributes,
2006 KIFONT::FONT* aFont,
2007 const KIFONT::METRICS& aFontMetrics,
2008 void* aData )
2009{
2010 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
2011
2012 if( gbr_metadata )
2013 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
2014
2015 PLOTTER::PlotText( aPos, aColor, aText, aAttributes, aFont, aFontMetrics, aData );
2016}
2017
2018
2020{
2021 if( aPositive )
2022 fprintf( m_outputFile, "%%LPD*%%\n" );
2023 else
2024 fprintf( m_outputFile, "%%LPC*%%\n" );
2025}
2026
2027
2028bool APER_MACRO_FREEPOLY::IsSamePoly( const std::vector<VECTOR2I>& aPolygon ) const
2029{
2030 return polyCompare( m_Corners, aPolygon );
2031}
2032
2033
2034void APER_MACRO_FREEPOLY::Format( FILE * aOutput, double aIu2GbrMacroUnit )
2035{
2036 // Write aperture header
2037 fprintf( aOutput, "%%AM%s%d*\n", AM_FREEPOLY_BASENAME, m_Id );
2038 fprintf( aOutput, "4,1,%d,", (int)m_Corners.size() );
2039
2040 // Insert a newline after curr_line_count_max coordinates.
2041 int curr_line_corner_count = 0;
2042 const int curr_line_count_max = 20; // <= 0 to disable newlines
2043
2044 for( size_t ii = 0; ii <= m_Corners.size(); ii++ )
2045 {
2046 int jj = ii;
2047
2048 if( ii >= m_Corners.size() )
2049 jj = 0;
2050
2051 // Note: parameter values are always mm or inches
2052 fprintf( aOutput, "%#f,%#f,",
2053 m_Corners[jj].x * aIu2GbrMacroUnit, -m_Corners[jj].y * aIu2GbrMacroUnit );
2054
2055 if( curr_line_count_max >= 0 && ++curr_line_corner_count >= curr_line_count_max )
2056 {
2057 fprintf( aOutput, "\n" );
2058 curr_line_corner_count = 0;
2059 }
2060 }
2061
2062 // output rotation parameter
2063 fputs( "$1*%\n", aOutput );
2064}
2065
2066
2067void APER_MACRO_FREEPOLY_LIST::Format( FILE * aOutput, double aIu2GbrMacroUnit )
2068{
2069 for( int idx = 0; idx < AmCount(); idx++ )
2070 m_AMList[idx].Format( aOutput, aIu2GbrMacroUnit );
2071}
2072
2073
2074void APER_MACRO_FREEPOLY_LIST::Append( const std::vector<VECTOR2I>& aPolygon )
2075{
2076 m_AMList.emplace_back( aPolygon, AmCount() );
2077}
2078
2079
2080int APER_MACRO_FREEPOLY_LIST::FindAm( const std::vector<VECTOR2I>& aPolygon ) const
2081{
2082 for( int idx = 0; idx < AmCount(); idx++ )
2083 {
2084 if( m_AMList[idx].IsSamePoly( aPolygon ) )
2085 return idx;
2086 }
2087
2088 return -1;
2089}
#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
@ ERROR_INSIDE
Definition: approximation.h:34
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
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:221
double Sin() const
Definition: eda_angle.h:170
double AsDegrees() const
Definition: eda_angle.h:113
bool IsCardinal() const
Definition: eda_angle.cpp:40
double AsRadians() const
Definition: eda_angle.h:117
bool IsCardinal90() const
Definition: eda_angle.cpp:54
double Cos() const
Definition: eda_angle.h:189
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:206
GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB GetApertureAttrib()
Definition: gbr_metadata.h:215
GBR_NETLIST_METADATA m_NetlistMetadata
An item to handle object attribute.
Definition: gbr_metadata.h:263
GBR_APERTURE_METADATA m_ApertureMetadata
An item to handle aperture attribute.
Definition: gbr_metadata.h:258
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:682
bool m_plotMirror
Definition: plotter.h:659
static const int USE_DEFAULT_LINE_WIDTH
Definition: plotter.h:109
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:181
void MoveTo(const VECTOR2I &pos)
Definition: plotter.h:244
void FinishTo(const VECTOR2I &pos)
Definition: plotter.h:254
double m_iuPerDeviceUnit
Definition: plotter.h:656
VECTOR2I m_plotOffset
Definition: plotter.h:658
virtual VECTOR2D userToDeviceCoordinates(const VECTOR2I &aCoordinate)
Modify coordinates according to the orientation, scale factor, and offsets trace.
Definition: plotter.cpp:91
VECTOR2I m_paperSize
Definition: plotter.h:680
void sketchOval(const VECTOR2I &aPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, int aWidth)
Definition: plotter.cpp:500
int GetPlotterArcHighDef() const
Definition: plotter.h:209
char m_penState
Definition: plotter.h:671
wxString m_creator
Definition: plotter.h:674
int m_currentPenWidth
Definition: plotter.h:670
double m_plotScale
Plot scale - chosen by the user (even implicitly with 'fit in a4')
Definition: plotter.h:648
FILE * m_outputFile
Output file.
Definition: plotter.h:665
void LineTo(const VECTOR2I &pos)
Definition: plotter.h:249
void PenFinish()
Definition: plotter.h:260
static const int DO_NOT_SET_LINE_WIDTH
Definition: plotter.h:108
virtual void PlotText(const VECTOR2I &aPos, const COLOR4D &aColor, const wxString &aText, const TEXT_ATTRIBUTES &aAttributes, KIFONT::FONT *aFont=nullptr, const KIFONT::METRICS &aFontMetrics=KIFONT::METRICS::Default(), void *aData=nullptr)
Definition: plotter.cpp:754
RENDER_SETTINGS * m_renderSettings
Definition: plotter.h:684
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:485
double m_IUsPerDecimil
Definition: plotter.h:654
virtual int GetCurrentLineWidth() const
Definition: plotter.h:148
const VECTOR2I & GetArcMid() const
Definition: shape_arc.h:118
bool IsClockwise() const
Definition: shape_arc.h:317
const VECTOR2I & GetP1() const
Definition: shape_arc.h:117
const VECTOR2I & GetP0() const
Definition: shape_arc.h:116
const VECTOR2I & GetCenter() const
Definition: shape_arc.cpp:849
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:401
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:403
static constexpr EDA_ANGLE ANGLE_270
Definition: eda_angle.h:406
static constexpr EDA_ANGLE ANGLE_360
Definition: eda_angle.h:407
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:405
FILL_T
Definition: eda_shape.h:56
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
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:390
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:398
GR_TEXT_H_ALIGN_T
This is API surface mapped to common.types.HorizontalAlignment.
GR_TEXT_V_ALIGN_T
This is API surface mapped to common.types.VertialAlignment.
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:229
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695