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#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( std::move( 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( std::move( 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, nullptr );
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, GetCurrentLineWidth() );
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 else if( aWidth != 0 ) // Draw the polyline/polygon outline
1099 {
1100 SetCurrentLineWidth( aWidth, gbr_metadata );
1101
1102 MoveTo( VECTOR2I( aPoly.CPoint( 0 ) ) );
1103
1104 for( int ii = 1; ii < aPoly.PointCount(); ii++ )
1105 {
1106 int arcindex = aPoly.ArcIndex( ii );
1107
1108 if( arcindex < 0 )
1109 {
1111 LineTo( VECTOR2I( aPoly.CPoint( ii ) ) );
1112 }
1113 else
1114 {
1115 const SHAPE_ARC& arc = aPoly.Arc( arcindex );
1116
1117 plotArc( arc, true );
1118
1119 // skip points on arcs, since we plot the arc itself
1120 while( ii+1 < aPoly.PointCount() && arcindex == aPoly.ArcIndex( ii+1 ) )
1121 ii++;
1122 }
1123 }
1124
1125 // Ensure the thick outline is closed for filled polygons
1126 // (if not filled, could be only a polyline)
1127 if( ( aPoly.CPoint( 0 ) != aPoly.CPoint( -1 ) ) && ( aPoly.IsClosed() || aFill != FILL_T::NO_FILL ) )
1128 LineTo( VECTOR2I( aPoly.CPoint( 0 ) ) );
1129
1130 PenFinish();
1131 }
1132}
1133
1134
1135void GERBER_PLOTTER::PlotPoly( const std::vector<VECTOR2I>& aCornerList, FILL_T aFill, int aWidth,
1136 void * aData )
1137{
1138 if( aCornerList.size() <= 1 )
1139 return;
1140
1141 // Gerber format does not know filled polygons with thick outline
1142 // Therefore, to plot a filled polygon with outline having a thickness,
1143 // one should plot outline as thick segments
1144 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1145
1146 if( gbr_metadata )
1147 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1148
1149 if( aFill != FILL_T::NO_FILL )
1150 {
1151 fmt::println( m_outputFile, "G36*" );
1152
1153 MoveTo( aCornerList[0] );
1154 fmt::println( m_outputFile, "G01*" ); // Set linear interpolation.
1155
1156 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1157 LineTo( aCornerList[ii] );
1158
1159 // If the polygon is not closed, close it:
1160 if( aCornerList[0] != aCornerList[aCornerList.size()-1] )
1161 FinishTo( aCornerList[0] );
1162
1163 fmt::println( m_outputFile, "G37*" );
1164 }
1165
1166 if( aWidth != 0 || aFill == FILL_T::NO_FILL ) // Draw the polyline/polygon outline
1167 {
1168 SetCurrentLineWidth( aWidth, gbr_metadata );
1169
1170 MoveTo( aCornerList[0] );
1171
1172 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1173 LineTo( aCornerList[ii] );
1174
1175 // Ensure the thick outline is closed for filled polygons
1176 // (if not filled, could be only a polyline)
1177 if( aFill != FILL_T::NO_FILL && ( aCornerList[aCornerList.size() - 1] != aCornerList[0] ) )
1178 LineTo( aCornerList[0] );
1179
1180 PenFinish();
1181 }
1182}
1183
1184
1185void GERBER_PLOTTER::ThickSegment( const VECTOR2I& start, const VECTOR2I& end, int width, void* aData )
1186{
1187 if( GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData ) )
1188 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1189
1190 SetCurrentLineWidth( width, aData );
1192}
1193
1194
1195void GERBER_PLOTTER::ThickArc( const VECTOR2D& aCentre, const EDA_ANGLE& aStartAngle,
1196 const EDA_ANGLE& aAngle, double aRadius, int aWidth, void* aData )
1197{
1198 if( GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData ) )
1199 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1200
1201 SetCurrentLineWidth( aWidth, aData );
1202 PLOTTER::ThickArc( aCentre, aStartAngle, aAngle, aRadius, DO_NOT_SET_LINE_WIDTH, aData );
1203}
1204
1205
1206void GERBER_PLOTTER::ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width, void* aData )
1207{
1208 if( GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData ) )
1209 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1210
1211 SetCurrentLineWidth( width, aData );
1212 PLOTTER::ThickRect( p1, p2, DO_NOT_SET_LINE_WIDTH, aData );
1213}
1214
1215
1216void GERBER_PLOTTER::ThickCircle( const VECTOR2I& pos, int diametre, int width, void* aData )
1217{
1218 if( GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData ) )
1219 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1220
1221 SetCurrentLineWidth( width, aData );
1222 PLOTTER::ThickCircle( pos, diametre, DO_NOT_SET_LINE_WIDTH, aData );
1223}
1224
1225
1226void GERBER_PLOTTER::FilledCircle( const VECTOR2I& pos, int diametre, void* aData )
1227{
1228 // A filled circle is a graphic item, not a pad.
1229 // So it is drawn, not flashed.
1230 if( GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData ) )
1231 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1232
1233 // Draw a circle of diameter = diameter/2 with a line thickness = radius,
1234 // To create a filled circle
1235 SetCurrentLineWidth( diametre/2, aData );
1236 Circle( pos, diametre/2, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1237}
1238
1239
1240void GERBER_PLOTTER::ThickPoly( const SHAPE_POLY_SET& aPoly, int aWidth, void* aData )
1241{
1242 if( GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData ) )
1243 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1244
1245 SetCurrentLineWidth( aWidth, aData );
1247}
1248
1249
1250void GERBER_PLOTTER::FlashPadCircle( const VECTOR2I& pos, int diametre, void* aData )
1251{
1252 VECTOR2I size( diametre, diametre );
1253 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1254
1255 selectApertureWithAttributes( pos, gbr_metadata, size, 0, ANGLE_0, APERTURE::AT_CIRCLE );
1256}
1257
1258
1259void GERBER_PLOTTER::FlashPadOval( const VECTOR2I& aPos, const VECTOR2I& aSize,
1260 const EDA_ANGLE& aOrient, void* aData )
1261{
1262 wxASSERT( m_outputFile );
1263
1264 VECTOR2I size( aSize );
1265 EDA_ANGLE orient( aOrient );
1266 orient.Normalize();
1267 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1268
1269 // Flash a vertical or horizontal shape (this is a basic aperture).
1270 if( orient.IsCardinal() )
1271 {
1272 if( orient.IsCardinal90() )
1273 std::swap( size.x, size.y );
1274
1275 selectApertureWithAttributes( aPos, gbr_metadata, size, 0, ANGLE_0, APERTURE::AT_OVAL );
1276 }
1277 else // Plot pad as region.
1278 // Only regions and flashed items accept a object attribute TO.P for the pin name
1279 {
1280#ifdef GBR_USE_MACROS_FOR_ROTATED_OVAL
1282#endif
1283 {
1284 m_hasApertureRotOval = true;
1285 // We are using a aperture macro that expect size.y < size.x
1286 // i.e draw a horizontal line for rotation = 0.0
1287 // size.x = length, size.y = width
1288 if( size.x < size.y )
1289 {
1290 std::swap( size.x, size.y );
1291 orient += ANGLE_90;
1292
1293 if( orient > ANGLE_180 )
1294 orient -= ANGLE_180;
1295 }
1296
1297 selectApertureWithAttributes( aPos, gbr_metadata, size, 0, orient,
1299 return;
1300 }
1301
1302 // Draw the oval as round rect pad with a radius = 50% min size)
1303 // In gerber file, it will be drawn as a region with arcs, and can be
1304 // detected as pads (similar to a flashed pad)
1305 FlashPadRoundRect( aPos, aSize, std::min( aSize.x, aSize.y ) / 2, orient, aData );
1306 }
1307}
1308
1309
1310void GERBER_PLOTTER::FlashPadRect( const VECTOR2I& pos, const VECTOR2I& aSize,
1311 const EDA_ANGLE& aOrient, void* aData )
1312{
1313 wxASSERT( m_outputFile );
1314
1315 VECTOR2I size( aSize );
1316 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1317
1318 // Horizontal / vertical rect can use a basic aperture (not a macro)
1319 // so use it for rotation n*90 deg
1320 if( aOrient.IsCardinal() )
1321 {
1322 // Build the not rotated equivalent shape:
1323 if( aOrient.IsCardinal90() )
1324 std::swap( size.x, size.y );
1325
1326 selectApertureWithAttributes( pos, gbr_metadata, size, 0, ANGLE_0, APERTURE::AT_RECT );
1327 }
1328 else
1329 {
1330#ifdef GBR_USE_MACROS_FOR_ROTATED_RECT
1332 {
1333 m_hasApertureRotRect = true;
1334
1335 selectApertureWithAttributes( pos, gbr_metadata, size, 0, aOrient,
1337 }
1338 else
1339#endif
1340 {
1341 // plot pad shape as Gerber region
1342 VECTOR2I coord[4];
1343
1344 // coord[0] is assumed the lower left
1345 // coord[1] is assumed the upper left
1346 // coord[2] is assumed the upper right
1347 // coord[3] is assumed the lower right
1348 coord[0].x = -size.x/2; // lower left
1349 coord[0].y = size.y/2;
1350 coord[1].x = -size.x/2; // upper left
1351 coord[1].y = -size.y/2;
1352 coord[2].x = size.x/2; // upper right
1353 coord[2].y = -size.y/2;
1354 coord[3].x = size.x/2; // lower right
1355 coord[3].y = size.y/2;
1356
1357 FlashPadTrapez( pos, coord, aOrient, aData );
1358 }
1359 }
1360}
1361
1362
1363void GERBER_PLOTTER::FlashPadRoundRect( const VECTOR2I& aPadPos, const VECTOR2I& aSize,
1364 int aCornerRadius, const EDA_ANGLE& aOrient, void* aData )
1365
1366{
1367 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1368
1369#ifdef GBR_USE_MACROS_FOR_ROUNDRECT
1371#endif
1372 {
1374
1375 selectApertureWithAttributes( aPadPos, gbr_metadata, aSize, aCornerRadius, aOrient,
1377 return;
1378 }
1379
1380 // A Pad RoundRect is plotted as a Gerber region.
1381 // Initialize region metadata:
1382 bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
1383
1384 if( gbr_metadata )
1385 {
1386 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1387 std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
1388
1389 if( !attrib.empty() )
1390 {
1391 fmt::print( m_outputFile, "{}", attrib );
1392 clearTA_AperFunction = true;
1393 }
1394 }
1395
1396 // Plot the region using arcs in corners.
1397 plotRoundRectAsRegion( aPadPos, aSize, aCornerRadius, aOrient );
1398
1399 // Clear the TA attribute, to avoid the next item to inherit it:
1400 if( clearTA_AperFunction )
1401 {
1402 if( m_useX2format )
1403 fmt::println( m_outputFile, "%TD.AperFunction*%" );
1404 else
1405 fmt::println( m_outputFile, "G04 #@! TD.AperFunction*" );
1406 }
1407}
1408
1409
1410void GERBER_PLOTTER::plotRoundRectAsRegion( const VECTOR2I& aRectCenter, const VECTOR2I& aSize,
1411 int aCornerRadius, const EDA_ANGLE& aOrient )
1412{
1413 // The region outline is generated by 4 sides and 4 90 deg arcs
1414 // 1 --- 2
1415 // | c |
1416 // 4 --- 3
1417
1418 // Note also in user coordinates the Y axis is from top to bottom
1419 // for historical reasons.
1420
1421 // A helper structure to handle outlines coordinates (segments and arcs)
1422 // in user coordinates
1423 struct RR_EDGE
1424 {
1425 VECTOR2I m_start;
1426 VECTOR2I m_end;
1427 VECTOR2I m_center;
1428 EDA_ANGLE m_arc_angle_start;
1429 };
1430
1431 int hsizeX = aSize.x/2;
1432 int hsizeY = aSize.y/2;
1433
1434 RR_EDGE curr_edge;
1435 std::vector<RR_EDGE> rr_outline;
1436
1437 rr_outline.reserve( 4 );
1438
1439 // Build outline coordinates, relative to rectangle center, rotation 0:
1440
1441 // Top left corner 1 (and 4 to 1 left vertical side @ x=-hsizeX)
1442 curr_edge.m_start.x = -hsizeX;
1443 curr_edge.m_start.y = hsizeY - aCornerRadius;
1444 curr_edge.m_end.x = curr_edge.m_start.x;
1445 curr_edge.m_end.y = -hsizeY + aCornerRadius;
1446 curr_edge.m_center.x = -hsizeX + aCornerRadius;
1447 curr_edge.m_center.y = curr_edge.m_end.y;
1448 curr_edge.m_arc_angle_start = aOrient + ANGLE_180;
1449
1450 rr_outline.push_back( curr_edge );
1451
1452 // Top right corner 2 (and 1 to 2 top horizontal side @ y=-hsizeY)
1453 curr_edge.m_start.x = -hsizeX + aCornerRadius;
1454 curr_edge.m_start.y = -hsizeY;
1455 curr_edge.m_end.x = hsizeX - aCornerRadius;
1456 curr_edge.m_end.y = curr_edge.m_start.y;
1457 curr_edge.m_center.x = curr_edge.m_end.x;
1458 curr_edge.m_center.y = -hsizeY + aCornerRadius;
1459 curr_edge.m_arc_angle_start = aOrient + ANGLE_90;
1460
1461 rr_outline.push_back( curr_edge );
1462
1463 // bottom right corner 3 (and 2 to 3 right vertical side @ x=hsizeX)
1464 curr_edge.m_start.x = hsizeX;
1465 curr_edge.m_start.y = -hsizeY + aCornerRadius;
1466 curr_edge.m_end.x = curr_edge.m_start.x;
1467 curr_edge.m_end.y = hsizeY - aCornerRadius;
1468 curr_edge.m_center.x = hsizeX - aCornerRadius;
1469 curr_edge.m_center.y = curr_edge.m_end.y;
1470 curr_edge.m_arc_angle_start = aOrient + ANGLE_0;
1471
1472 rr_outline.push_back( curr_edge );
1473
1474 // bottom left corner 4 (and 3 to 4 bottom horizontal side @ y=hsizeY)
1475 curr_edge.m_start.x = hsizeX - aCornerRadius;
1476 curr_edge.m_start.y = hsizeY;
1477 curr_edge.m_end.x = -hsizeX + aCornerRadius;
1478 curr_edge.m_end.y = curr_edge.m_start.y;
1479 curr_edge.m_center.x = curr_edge.m_end.x;
1480 curr_edge.m_center.y = hsizeY - aCornerRadius;
1481 curr_edge.m_arc_angle_start = aOrient - ANGLE_90;
1482
1483 rr_outline.push_back( curr_edge );
1484
1485 // Move relative coordinates to the actual location and rotation:
1486 VECTOR2I arc_last_center;
1487 EDA_ANGLE arc_last_angle = curr_edge.m_arc_angle_start - ANGLE_90;
1488
1489 for( RR_EDGE& rr_edge: rr_outline )
1490 {
1491 RotatePoint( rr_edge.m_start, aOrient );
1492 RotatePoint( rr_edge.m_end, aOrient );
1493 RotatePoint( rr_edge.m_center, aOrient );
1494 rr_edge.m_start += aRectCenter;
1495 rr_edge.m_end += aRectCenter;
1496 rr_edge.m_center += aRectCenter;
1497 arc_last_center = rr_edge.m_center;
1498 }
1499
1500 // Ensure the region is a closed polygon, i.e. the end point of last segment
1501 // (end of arc) is the same as the first point. Rounding issues can create a
1502 // small difference, mainly for rotated pads.
1503 // calculate last point (end of last arc):
1504 VECTOR2I last_pt;
1505 last_pt.x = arc_last_center.x + KiROUND( aCornerRadius * arc_last_angle.Cos() );
1506 last_pt.y = arc_last_center.y - KiROUND( aCornerRadius * arc_last_angle.Sin() );
1507
1508 VECTOR2I first_pt = rr_outline[0].m_start;
1509
1510#if 0 // For test only:
1511 if( last_pt != first_pt )
1512 wxLogMessage( wxS( "first pt %d %d last pt %d %d" ),
1513 first_pt.x, first_pt.y, last_pt.x, last_pt.y );
1514#endif
1515
1516 fmt::println( m_outputFile, "G36*" ); // Start region
1517 fmt::println( m_outputFile, "G01*" ); // Set linear interpolation.
1518 first_pt = last_pt;
1519 MoveTo( first_pt ); // Start point of region, must be same as end point
1520
1521 for( RR_EDGE& rr_edge: rr_outline )
1522 {
1523 if( aCornerRadius ) // Guard: ensure we do not create arcs with radius = 0
1524 {
1525 // LineTo( rr_edge.m_end ); // made in plotArc()
1526 plotArc( rr_edge.m_center, -rr_edge.m_arc_angle_start,
1527 -rr_edge.m_arc_angle_start + ANGLE_90, aCornerRadius, true );
1528 }
1529 else
1530 {
1531 LineTo( rr_edge.m_end );
1532 }
1533 }
1534
1535 fmt::println( m_outputFile, "G37*" ); // Close region
1536}
1537
1538
1539void GERBER_PLOTTER::FlashPadCustom( const VECTOR2I& aPadPos, const VECTOR2I& aSize,
1540 const EDA_ANGLE& aOrient, SHAPE_POLY_SET* aPolygons,
1541 void* aData )
1542{
1543 // A Pad custom is plotted as polygon (a region in Gerber language).
1544 GBR_METADATA gbr_metadata;
1545
1546 if( aData )
1547 gbr_metadata = *static_cast<GBR_METADATA*>( aData );
1548
1549 SHAPE_POLY_SET polyshape = aPolygons->CloneDropTriangulation();
1550
1551 std::vector<VECTOR2I> cornerList;
1552
1553 for( int cnt = 0; cnt < polyshape.OutlineCount(); ++cnt )
1554 {
1555 SHAPE_LINE_CHAIN& poly = polyshape.Outline( cnt );
1556
1557 cornerList.clear();
1558
1559 for( int ii = 0; ii < poly.PointCount(); ++ii )
1560 cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
1561
1562 // Close polygon
1563 cornerList.push_back( cornerList[0] );
1564
1565#ifdef GBR_USE_MACROS_FOR_CUSTOM_PAD
1567 {
1568 PlotGerberRegion( cornerList, &gbr_metadata );
1569 }
1570 else
1571 {
1572 // An AM will be created. the shape must be in position 0,0 and orientation 0
1573 // to be able to reuse the same AM for pads having the same shape
1574 for( size_t ii = 0; ii < cornerList.size(); ii++ )
1575 {
1576 cornerList[ii] -= aPadPos;
1577 RotatePoint( cornerList[ii], -aOrient );
1578 }
1579
1580 VECTOR2D pos_dev = userToDeviceCoordinates( aPadPos );
1581 selectAperture( cornerList, aOrient, APERTURE::AM_FREE_POLYGON,
1582 gbr_metadata.GetApertureAttrib(),
1583 gbr_metadata.GetCustomAttribute() );
1584 formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1585
1586 emitDcode( pos_dev, 3 );
1587 }
1588#else
1589 PlotGerberRegion( cornerList, &gbr_metadata );
1590#endif
1591 }
1592}
1593
1594
1595void GERBER_PLOTTER::FlashPadChamferRoundRect( const VECTOR2I& aShapePos, const VECTOR2I& aPadSize,
1596 int aCornerRadius, double aChamferRatio,
1597 int aChamferPositions, const EDA_ANGLE& aPadOrient,
1598 void* aData )
1599{
1600 GBR_METADATA gbr_metadata;
1601
1602 if( aData )
1603 gbr_metadata = *static_cast<GBR_METADATA*>( aData );
1604
1605 VECTOR2D pos_device = userToDeviceCoordinates( aShapePos );
1606 SHAPE_POLY_SET outline;
1607 std::vector<VECTOR2I> cornerList;
1608
1609 bool hasRoundedCorner = aCornerRadius != 0 && aChamferPositions != 15;
1610
1611#ifdef GBR_USE_MACROS_FOR_CHAMFERED_RECT
1612 // Round rect shape or Apert Macros disabled
1613 if( hasRoundedCorner || m_gerberDisableApertMacros )
1614#endif
1615 {
1616 TransformRoundChamferedRectToPolygon( outline, aShapePos, aPadSize, aPadOrient,
1617 aCornerRadius, aChamferRatio, aChamferPositions, 0,
1619
1620 // Build the corner list
1621 const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
1622
1623 for( int ii = 0; ii < corners.PointCount(); ii++ )
1624 cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
1625
1626 // Close the polygon
1627 cornerList.push_back( cornerList[0] );
1628
1629#ifdef GBR_USE_MACROS_FOR_CHAMFERED_ROUND_RECT
1631 {
1632 PlotGerberRegion( cornerList, &gbr_metadata );
1633 }
1634 else
1635 {
1636 // An AM will be created. the shape must be in position 0,0 and orientation 0
1637 // to be able to reuse the same AM for pads having the same shape
1638 for( size_t ii = 0; ii < cornerList.size(); ii++ )
1639 {
1640 cornerList[ii] -= aShapePos;
1641 RotatePoint( cornerList[ii], -aPadOrient );
1642 }
1643
1644 selectAperture( cornerList, aPadOrient, APERTURE::AM_FREE_POLYGON,
1645 gbr_metadata.GetApertureAttrib(),
1646 gbr_metadata.GetCustomAttribute() );
1647 formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1648
1649 emitDcode( pos_device, 3 );
1650 }
1651#else
1652 PlotGerberRegion( cornerList, &gbr_metadata );
1653#endif
1654
1655 return;
1656 }
1657
1658 // Build the chamfered polygon (4 to 8 corners )
1659 TransformRoundChamferedRectToPolygon( outline, VECTOR2I( 0, 0 ), aPadSize, ANGLE_0, 0,
1660 aChamferRatio, aChamferPositions, 0,
1662
1663 // Build the corner list
1664 const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
1665
1666 // Generate the polygon (4 to 8 corners )
1667 for( int ii = 0; ii < corners.PointCount(); ii++ )
1668 cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
1669
1670 switch( cornerList.size() )
1671 {
1672 case 4:
1674 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE4P,
1675 gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
1676 break;
1677
1678 case 5:
1680 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE5P,
1681 gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
1682 break;
1683
1684 case 6:
1686 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE6P,
1687 gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
1688 break;
1689
1690 case 7:
1692 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE7P,
1693 gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
1694 break;
1695
1696 case 8:
1698 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE8P,
1699 gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
1700 break;
1701
1702 default:
1703 wxLogMessage( wxS( "FlashPadChamferRoundRect(): Unexpected number of corners (%d)" ),
1704 (int)cornerList.size() );
1705 break;
1706 }
1707
1708 formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1709
1710 emitDcode( pos_device, 3 );
1711}
1712
1713
1714void GERBER_PLOTTER::FlashPadTrapez( const VECTOR2I& aPadPos, const VECTOR2I* aCorners,
1715 const EDA_ANGLE& aPadOrient, void* aData )
1716{
1717 // polygon corners list
1718 std::vector<VECTOR2I> cornerList = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
1719
1720 // Draw the polygon and fill the interior as required
1721 for( unsigned ii = 0; ii < 4; ii++ )
1722 {
1723 RotatePoint( cornerList[ii], aPadOrient );
1724 cornerList[ii] += aPadPos;
1725 }
1726
1727 // Close the polygon
1728 cornerList.push_back( cornerList[0] );
1729
1730 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1731 GBR_METADATA metadata;
1732
1733 if( gbr_metadata )
1734 metadata = *gbr_metadata;
1735
1736 // Plot a filled polygon:
1737#ifdef GBR_USE_MACROS_FOR_TRAPEZOID
1739#endif
1740 {
1742 VECTOR2D pos_dev = userToDeviceCoordinates( aPadPos );
1743
1744 // polygon corners list
1745 std::vector<VECTOR2I> corners = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
1746 int aperture_attribute = 0;
1747 std::string custom_attribute = "";
1748
1749 if( gbr_metadata )
1750 {
1751 aperture_attribute = gbr_metadata->GetApertureAttrib();
1752 custom_attribute = gbr_metadata->GetCustomAttribute();
1753 }
1754
1755 selectAperture( corners, aPadOrient, APERTURE::APER_MACRO_OUTLINE4P, aperture_attribute,
1756 custom_attribute );
1757
1758 if( gbr_metadata )
1759 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1760
1761 emitDcode( pos_dev, 3 );
1762 return;
1763 }
1764
1765 PlotGerberRegion( cornerList, &metadata );
1766}
1767
1768
1769void GERBER_PLOTTER::FlashRegularPolygon( const VECTOR2I& aShapePos, int aDiameter,
1770 int aCornerCount, const EDA_ANGLE& aOrient,
1771 void* aData )
1772{
1773 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1774
1775 GBR_METADATA metadata;
1776
1777 if( gbr_metadata )
1778 metadata = *gbr_metadata;
1779
1780 APERTURE::APERTURE_TYPE apert_type =
1781 ( APERTURE::APERTURE_TYPE )( APERTURE::AT_REGULAR_POLY3 + aCornerCount - 3 );
1782
1783 wxASSERT( apert_type >= APERTURE::APERTURE_TYPE::AT_REGULAR_POLY3
1785
1786 selectApertureWithAttributes( aShapePos, gbr_metadata, VECTOR2I( 0, 0 ), aDiameter / 2,
1787 aOrient, apert_type );
1788}
1789
1790
1792 const COLOR4D& aColor,
1793 const wxString& aText,
1794 const EDA_ANGLE& aOrient,
1795 const VECTOR2I& aSize,
1796 enum GR_TEXT_H_ALIGN_T aH_justify,
1797 enum GR_TEXT_V_ALIGN_T aV_justify,
1798 int aWidth,
1799 bool aItalic,
1800 bool aBold,
1801 bool aMultilineAllowed,
1802 KIFONT::FONT* aFont,
1803 const KIFONT::METRICS& aFontMetrics,
1804 void* aData )
1805{
1806 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1807
1808 if( gbr_metadata )
1809 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1810
1811 PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth,
1812 aItalic, aBold, aMultilineAllowed, aFont, aFontMetrics, aData );
1813}
1814
1815
1817 const COLOR4D& aColor,
1818 const wxString& aText,
1819 const TEXT_ATTRIBUTES& aAttributes,
1820 KIFONT::FONT* aFont,
1821 const KIFONT::METRICS& aFontMetrics,
1822 void* aData )
1823{
1824 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1825
1826 if( gbr_metadata )
1827 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1828
1829 PLOTTER::PlotText( aPos, aColor, aText, aAttributes, aFont, aFontMetrics, aData );
1830}
1831
1832
1834{
1835 if( aPositive )
1836 fmt::println( m_outputFile, "%LPD*%" );
1837 else
1838 fmt::println( m_outputFile, "%LPC*%" );
1839}
1840
1841
1842bool APER_MACRO_FREEPOLY::IsSamePoly( const std::vector<VECTOR2I>& aPolygon ) const
1843{
1844 return polyCompare( m_Corners, aPolygon );
1845}
1846
1847
1848void APER_MACRO_FREEPOLY::Format( FILE * aOutput, double aIu2GbrMacroUnit )
1849{
1850 // Write aperture header
1851 fmt::println( aOutput, "%AM{}{}*", AM_FREEPOLY_BASENAME, m_Id );
1852 fmt::print( aOutput, "4,1,{},", (int) m_Corners.size() );
1853
1854 // Insert a newline after curr_line_count_max coordinates.
1855 int curr_line_corner_count = 0;
1856 const int curr_line_count_max = 20; // <= 0 to disable newlines
1857
1858 for( size_t ii = 0; ii <= m_Corners.size(); ii++ )
1859 {
1860 int jj = ii;
1861
1862 if( ii >= m_Corners.size() )
1863 jj = 0;
1864
1865 // Note: parameter values are always mm or inches
1866 fmt::print( aOutput, "{:#f},{:#f},",
1867 m_Corners[jj].x * aIu2GbrMacroUnit, -m_Corners[jj].y * aIu2GbrMacroUnit );
1868
1869 if( curr_line_count_max >= 0 && ++curr_line_corner_count >= curr_line_count_max )
1870 {
1871 fmt::println( aOutput, "" );
1872 curr_line_corner_count = 0;
1873 }
1874 }
1875
1876 // output rotation parameter
1877 fmt::println( aOutput, "$1*%" );
1878}
1879
1880
1881void APER_MACRO_FREEPOLY_LIST::Format( FILE * aOutput, double aIu2GbrMacroUnit )
1882{
1883 for( int idx = 0; idx < AmCount(); idx++ )
1884 m_AMList[idx].Format( aOutput, aIu2GbrMacroUnit );
1885}
1886
1887
1888void APER_MACRO_FREEPOLY_LIST::Append( const std::vector<VECTOR2I>& aPolygon )
1889{
1890 m_AMList.emplace_back( aPolygon, AmCount() );
1891}
1892
1893
1894int APER_MACRO_FREEPOLY_LIST::FindAm( const std::vector<VECTOR2I>& aPolygon ) const
1895{
1896 for( int idx = 0; idx < AmCount(); idx++ )
1897 {
1898 if( m_AMList[idx].IsSamePoly( aPolygon ) )
1899 return idx;
1900 }
1901
1902 return -1;
1903}
#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:229
double Sin() const
Definition: eda_angle.h:178
double AsDegrees() const
Definition: eda_angle.h:116
bool IsCardinal() const
Definition: eda_angle.cpp:40
double AsRadians() const
Definition: eda_angle.h:120
bool IsCardinal90() const
Definition: eda_angle.cpp:54
double Cos() const
Definition: eda_angle.h:197
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 FlashPadRect(const VECTOR2I &aPadPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, void *aData) override
std::string m_objectAttributesDictionary
virtual void ThickSegment(const VECTOR2I &start, const VECTOR2I &end, int width, void *aData) override
virtual void SetGerberCoordinatesFormat(int aResolution, bool aUseInches=false) override
Selection of Gerber units and resolution (number of digits in mantissa).
void FlashPadChamferRoundRect(const VECTOR2I &aShapePos, const VECTOR2I &aPadSize, int aCornerRadius, double aChamferRatio, int aChamferPositions, const EDA_ANGLE &aPadOrient, void *aData)
Flash a chamfered round rect pad.
void ClearAllAttributes()
Remove (clear) all attributes from object attributes dictionary (TO.
int GetOrCreateAperture(const VECTOR2I &aSize, int aRadius, const EDA_ANGLE &aRotation, APERTURE::APERTURE_TYPE aType, int aApertureAttribute, const std::string &aCustomAttribute)
virtual void ThickPoly(const SHAPE_POLY_SET &aPoly, int aWidth, void *aData) override
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 FlashPadCircle(const VECTOR2I &pos, int diametre, void *aData) override
Filled circular flashes are stored as apertures.
virtual void FlashPadRoundRect(const VECTOR2I &aPadPos, const VECTOR2I &aSize, int aCornerRadius, const EDA_ANGLE &aOrient, void *aData) override
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.
virtual void FlashRegularPolygon(const VECTOR2I &aShapePos, int aDiameter, int aCornerCount, const EDA_ANGLE &aOrient, void *aData) override
Flash a regular polygon.
APER_MACRO_FREEPOLY_LIST m_am_freepoly_list
virtual void PlotPoly(const std::vector< VECTOR2I > &aCornerList, FILL_T aFill, int aWidth, void *aData) override
Gerber polygon: they can (and should) be filled with the appropriate G36/G37 sequence.
virtual void FlashPadOval(const VECTOR2I &aPadPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, void *aData) override
std::vector< APERTURE > m_apertures
bool m_hasApertureChamferedRect
virtual void FlashPadTrapez(const VECTOR2I &aPadPos, const VECTOR2I *aCorners, const EDA_ANGLE &aPadOrient, 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 Circle(const VECTOR2I &pos, int diametre, FILL_T fill, int width) override
virtual void Arc(const VECTOR2D &aCenter, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aAngle, double aRadius, FILL_T aFill, int aWidth) override
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
virtual void Rect(const VECTOR2I &p1, const VECTOR2I &p2, FILL_T fill, int width) 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 ThickRect(const VECTOR2I &p1, const VECTOR2I &p2, int width, void *aData) override
virtual void SetLayerPolarity(bool aPositive) override
Change the plot polarity and begin a new layer.
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 FlashPadCustom(const VECTOR2I &aPadPos, const VECTOR2I &aSize, const EDA_ANGLE &aPadOrient, SHAPE_POLY_SET *aPolygons, void *aData) override
virtual void EndBlock(void *aData) override
Define the end of a group of drawing items the group is started by StartBlock().
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 ThickArc(const VECTOR2D &aCentre, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aAngle, double aRadius, int aWidth, void *aData) override
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, void *aData) 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.
virtual void ThickCircle(const VECTOR2I &pos, int diametre, int width, void *aData) override
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:677
virtual void ThickPoly(const SHAPE_POLY_SET &aPoly, int aWidth, void *aData)
Definition: plotter.cpp:603
bool m_plotMirror
Definition: plotter.h:654
static const int USE_DEFAULT_LINE_WIDTH
Definition: plotter.h:125
void MoveTo(const VECTOR2I &pos)
Definition: plotter.h:261
void FinishTo(const VECTOR2I &pos)
Definition: plotter.h:271
virtual void polyArc(const VECTOR2D &aCentre, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aAngle, double aRadius, FILL_T aFill, int aWidth)
Generic fallback: arc rendered as a polyline.
Definition: plotter.cpp:181
double m_iuPerDeviceUnit
Definition: plotter.h:651
VECTOR2I m_plotOffset
Definition: plotter.h:653
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:675
virtual void ThickArc(const EDA_SHAPE &aArcShape, void *aData, int aWidth)
Definition: plotter.cpp:560
int GetPlotterArcHighDef() const
Definition: plotter.h:228
char m_penState
Definition: plotter.h:666
wxString m_creator
Definition: plotter.h:669
int m_currentPenWidth
Definition: plotter.h:665
double m_plotScale
Plot scale - chosen by the user (even implicitly with 'fit in a4')
Definition: plotter.h:643
FILE * m_outputFile
Output file.
Definition: plotter.h:660
void LineTo(const VECTOR2I &pos)
Definition: plotter.h:266
void PenFinish()
Definition: plotter.h:277
static const int DO_NOT_SET_LINE_WIDTH
Definition: plotter.h:124
virtual void ThickSegment(const VECTOR2I &start, const VECTOR2I &end, int width, void *aData)
Definition: plotter.cpp:538
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:687
virtual void ThickRect(const VECTOR2I &p1, const VECTOR2I &p2, int width, void *aData)
Definition: plotter.cpp:585
RENDER_SETTINGS * m_renderSettings
Definition: plotter.h:679
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:624
double m_IUsPerDecimil
Definition: plotter.h:649
virtual void ThickCircle(const VECTOR2I &pos, int diametre, int width, void *aData)
Definition: plotter.cpp:591
virtual int GetCurrentLineWidth() const
Definition: plotter.h:167
const VECTOR2I & GetArcMid() const
Definition: shape_arc.h:118
bool IsClockwise() const
Definition: shape_arc.h:318
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:411
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:413
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:415
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
This file contains miscellaneous commonly used macros and functions.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:400
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: string_utils.h:429
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