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