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