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 );
1196
1197 // A zero-length segment is plotted as a single flash of the segment's aperture.
1198 // Falling through to PLOTTER::ThickSegment would emit a filled circle stroked with
1199 // the (now sentinel) DO_NOT_SET_LINE_WIDTH width, creating a spurious 0-size aperture.
1200 if( start == end )
1201 {
1202 MoveTo( start );
1203 FinishTo( end );
1204 return;
1205 }
1206
1208}
1209
1210
1211void GERBER_PLOTTER::ThickArc( const VECTOR2D& aCentre, const EDA_ANGLE& aStartAngle,
1212 const EDA_ANGLE& aAngle, double aRadius, int aWidth, void* aData )
1213{
1214 if( GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData ) )
1215 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1216
1217 SetCurrentLineWidth( aWidth, aData );
1218 PLOTTER::ThickArc( aCentre, aStartAngle, aAngle, aRadius, DO_NOT_SET_LINE_WIDTH, aData );
1219}
1220
1221
1222void GERBER_PLOTTER::ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width, void* aData )
1223{
1224 if( GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData ) )
1225 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1226
1227 SetCurrentLineWidth( width, aData );
1228 PLOTTER::ThickRect( p1, p2, DO_NOT_SET_LINE_WIDTH, aData );
1229}
1230
1231
1232void GERBER_PLOTTER::ThickCircle( const VECTOR2I& pos, int diametre, int width, void* aData )
1233{
1234 if( GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData ) )
1235 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1236
1237 SetCurrentLineWidth( width, aData );
1238 PLOTTER::ThickCircle( pos, diametre, DO_NOT_SET_LINE_WIDTH, aData );
1239}
1240
1241
1242void GERBER_PLOTTER::FilledCircle( const VECTOR2I& pos, int diametre, void* aData )
1243{
1244 // A filled circle is a graphic item, not a pad.
1245 // So it is drawn, not flashed.
1246 if( GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData ) )
1247 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1248
1249 // Draw a circle of diameter = diameter/2 with a line thickness = radius,
1250 // To create a filled circle
1251 SetCurrentLineWidth( diametre/2, aData );
1252 Circle( pos, diametre/2, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1253}
1254
1255
1256void GERBER_PLOTTER::ThickPoly( const SHAPE_POLY_SET& aPoly, int aWidth, void* aData )
1257{
1258 if( GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData ) )
1259 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1260
1261 SetCurrentLineWidth( aWidth, aData );
1263}
1264
1265
1266void GERBER_PLOTTER::FlashPadCircle( const VECTOR2I& pos, int diametre, void* aData )
1267{
1268 VECTOR2I size( diametre, diametre );
1269 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1270
1271 selectApertureWithAttributes( pos, gbr_metadata, size, 0, ANGLE_0, APERTURE::AT_CIRCLE );
1272}
1273
1274
1275void GERBER_PLOTTER::FlashPadOval( const VECTOR2I& aPos, const VECTOR2I& aSize,
1276 const EDA_ANGLE& aOrient, void* aData )
1277{
1278 wxASSERT( m_outputFile );
1279
1280 VECTOR2I size( aSize );
1281 EDA_ANGLE orient( aOrient );
1282 orient.Normalize();
1283 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1284
1285 // Flash a vertical or horizontal shape (this is a basic aperture).
1286 if( orient.IsCardinal() )
1287 {
1288 if( orient.IsCardinal90() )
1289 std::swap( size.x, size.y );
1290
1291 selectApertureWithAttributes( aPos, gbr_metadata, size, 0, ANGLE_0, APERTURE::AT_OVAL );
1292 }
1293 else // Plot pad as region.
1294 // Only regions and flashed items accept a object attribute TO.P for the pin name
1295 {
1296#ifdef GBR_USE_MACROS_FOR_ROTATED_OVAL
1298#endif
1299 {
1300 m_hasApertureRotOval = true;
1301 // We are using a aperture macro that expect size.y < size.x
1302 // i.e draw a horizontal line for rotation = 0.0
1303 // size.x = length, size.y = width
1304 if( size.x < size.y )
1305 {
1306 std::swap( size.x, size.y );
1307 orient += ANGLE_90;
1308
1309 if( orient > ANGLE_180 )
1310 orient -= ANGLE_180;
1311 }
1312
1313 selectApertureWithAttributes( aPos, gbr_metadata, size, 0, orient,
1315 return;
1316 }
1317
1318 // Draw the oval as round rect pad with a radius = 50% min size)
1319 // In gerber file, it will be drawn as a region with arcs, and can be
1320 // detected as pads (similar to a flashed pad)
1321 FlashPadRoundRect( aPos, aSize, std::min( aSize.x, aSize.y ) / 2, orient, aData );
1322 }
1323}
1324
1325
1326void GERBER_PLOTTER::FlashPadRect( const VECTOR2I& pos, const VECTOR2I& aSize,
1327 const EDA_ANGLE& aOrient, void* aData )
1328{
1329 wxASSERT( m_outputFile );
1330
1331 VECTOR2I size( aSize );
1332 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1333
1334 // Horizontal / vertical rect can use a basic aperture (not a macro)
1335 // so use it for rotation n*90 deg
1336 if( aOrient.IsCardinal() )
1337 {
1338 // Build the not rotated equivalent shape:
1339 if( aOrient.IsCardinal90() )
1340 std::swap( size.x, size.y );
1341
1342 selectApertureWithAttributes( pos, gbr_metadata, size, 0, ANGLE_0, APERTURE::AT_RECT );
1343 }
1344 else
1345 {
1346#ifdef GBR_USE_MACROS_FOR_ROTATED_RECT
1348 {
1349 m_hasApertureRotRect = true;
1350
1351 selectApertureWithAttributes( pos, gbr_metadata, size, 0, aOrient,
1353 }
1354 else
1355#endif
1356 {
1357 // plot pad shape as Gerber region
1358 VECTOR2I coord[4];
1359
1360 // coord[0] is assumed the lower left
1361 // coord[1] is assumed the upper left
1362 // coord[2] is assumed the upper right
1363 // coord[3] is assumed the lower right
1364 coord[0].x = -size.x/2; // lower left
1365 coord[0].y = size.y/2;
1366 coord[1].x = -size.x/2; // upper left
1367 coord[1].y = -size.y/2;
1368 coord[2].x = size.x/2; // upper right
1369 coord[2].y = -size.y/2;
1370 coord[3].x = size.x/2; // lower right
1371 coord[3].y = size.y/2;
1372
1373 FlashPadTrapez( pos, coord, aOrient, aData );
1374 }
1375 }
1376}
1377
1378
1379void GERBER_PLOTTER::FlashPadRoundRect( const VECTOR2I& aPadPos, const VECTOR2I& aSize,
1380 int aCornerRadius, const EDA_ANGLE& aOrient, void* aData )
1381
1382{
1383 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1384
1385#ifdef GBR_USE_MACROS_FOR_ROUNDRECT
1387#endif
1388 {
1390
1391 selectApertureWithAttributes( aPadPos, gbr_metadata, aSize, aCornerRadius, aOrient,
1393 return;
1394 }
1395
1396 // A Pad RoundRect is plotted as a Gerber region.
1397 // Initialize region metadata:
1398 bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
1399
1400 if( gbr_metadata )
1401 {
1402 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1403 std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
1404
1405 if( !attrib.empty() )
1406 {
1407 fmt::print( m_outputFile, "{}", attrib );
1408 clearTA_AperFunction = true;
1409 }
1410 }
1411
1412 // Plot the region using arcs in corners.
1413 plotRoundRectAsRegion( aPadPos, aSize, aCornerRadius, aOrient );
1414
1415 // Clear the TA attribute, to avoid the next item to inherit it:
1416 if( clearTA_AperFunction )
1417 {
1418 if( m_useX2format )
1419 fmt::println( m_outputFile, "%TD.AperFunction*%" );
1420 else
1421 fmt::println( m_outputFile, "G04 #@! TD.AperFunction*" );
1422 }
1423}
1424
1425
1426void GERBER_PLOTTER::plotRoundRectAsRegion( const VECTOR2I& aRectCenter, const VECTOR2I& aSize,
1427 int aCornerRadius, const EDA_ANGLE& aOrient )
1428{
1429 // The region outline is generated by 4 sides and 4 90 deg arcs
1430 // 1 --- 2
1431 // | c |
1432 // 4 --- 3
1433
1434 // Note also in user coordinates the Y axis is from top to bottom
1435 // for historical reasons.
1436
1437 // A helper structure to handle outlines coordinates (segments and arcs)
1438 // in user coordinates
1439 struct RR_EDGE
1440 {
1441 VECTOR2I m_start;
1442 VECTOR2I m_end;
1443 VECTOR2I m_center;
1444 EDA_ANGLE m_arc_angle_start;
1445 };
1446
1447 int hsizeX = aSize.x/2;
1448 int hsizeY = aSize.y/2;
1449
1450 RR_EDGE curr_edge;
1451 std::vector<RR_EDGE> rr_outline;
1452
1453 rr_outline.reserve( 4 );
1454
1455 // Build outline coordinates, relative to rectangle center, rotation 0:
1456
1457 // Top left corner 1 (and 4 to 1 left vertical side @ x=-hsizeX)
1458 curr_edge.m_start.x = -hsizeX;
1459 curr_edge.m_start.y = hsizeY - aCornerRadius;
1460 curr_edge.m_end.x = curr_edge.m_start.x;
1461 curr_edge.m_end.y = -hsizeY + aCornerRadius;
1462 curr_edge.m_center.x = -hsizeX + aCornerRadius;
1463 curr_edge.m_center.y = curr_edge.m_end.y;
1464 curr_edge.m_arc_angle_start = aOrient + ANGLE_180;
1465
1466 rr_outline.push_back( curr_edge );
1467
1468 // Top right corner 2 (and 1 to 2 top horizontal side @ y=-hsizeY)
1469 curr_edge.m_start.x = -hsizeX + aCornerRadius;
1470 curr_edge.m_start.y = -hsizeY;
1471 curr_edge.m_end.x = hsizeX - aCornerRadius;
1472 curr_edge.m_end.y = curr_edge.m_start.y;
1473 curr_edge.m_center.x = curr_edge.m_end.x;
1474 curr_edge.m_center.y = -hsizeY + aCornerRadius;
1475 curr_edge.m_arc_angle_start = aOrient + ANGLE_90;
1476
1477 rr_outline.push_back( curr_edge );
1478
1479 // bottom right corner 3 (and 2 to 3 right vertical side @ x=hsizeX)
1480 curr_edge.m_start.x = hsizeX;
1481 curr_edge.m_start.y = -hsizeY + aCornerRadius;
1482 curr_edge.m_end.x = curr_edge.m_start.x;
1483 curr_edge.m_end.y = hsizeY - aCornerRadius;
1484 curr_edge.m_center.x = hsizeX - aCornerRadius;
1485 curr_edge.m_center.y = curr_edge.m_end.y;
1486 curr_edge.m_arc_angle_start = aOrient + ANGLE_0;
1487
1488 rr_outline.push_back( curr_edge );
1489
1490 // bottom left corner 4 (and 3 to 4 bottom horizontal side @ y=hsizeY)
1491 curr_edge.m_start.x = hsizeX - aCornerRadius;
1492 curr_edge.m_start.y = hsizeY;
1493 curr_edge.m_end.x = -hsizeX + aCornerRadius;
1494 curr_edge.m_end.y = curr_edge.m_start.y;
1495 curr_edge.m_center.x = curr_edge.m_end.x;
1496 curr_edge.m_center.y = hsizeY - aCornerRadius;
1497 curr_edge.m_arc_angle_start = aOrient - ANGLE_90;
1498
1499 rr_outline.push_back( curr_edge );
1500
1501 // Move relative coordinates to the actual location and rotation:
1502 VECTOR2I arc_last_center;
1503 EDA_ANGLE arc_last_angle = curr_edge.m_arc_angle_start - ANGLE_90;
1504
1505 for( RR_EDGE& rr_edge: rr_outline )
1506 {
1507 RotatePoint( rr_edge.m_start, aOrient );
1508 RotatePoint( rr_edge.m_end, aOrient );
1509 RotatePoint( rr_edge.m_center, aOrient );
1510 rr_edge.m_start += aRectCenter;
1511 rr_edge.m_end += aRectCenter;
1512 rr_edge.m_center += aRectCenter;
1513 arc_last_center = rr_edge.m_center;
1514 }
1515
1516 // Ensure the region is a closed polygon, i.e. the end point of last segment
1517 // (end of arc) is the same as the first point. Rounding issues can create a
1518 // small difference, mainly for rotated pads.
1519 // calculate last point (end of last arc):
1520 VECTOR2I last_pt;
1521 last_pt.x = arc_last_center.x + KiROUND( aCornerRadius * arc_last_angle.Cos() );
1522 last_pt.y = arc_last_center.y - KiROUND( aCornerRadius * arc_last_angle.Sin() );
1523
1524 VECTOR2I first_pt = rr_outline[0].m_start;
1525
1526#if 0 // For test only:
1527 if( last_pt != first_pt )
1528 wxLogMessage( wxS( "first pt %d %d last pt %d %d" ),
1529 first_pt.x, first_pt.y, last_pt.x, last_pt.y );
1530#endif
1531
1532 fmt::println( m_outputFile, "G36*" ); // Start region
1533 fmt::println( m_outputFile, "G01*" ); // Set linear interpolation.
1534 first_pt = last_pt;
1535 MoveTo( first_pt ); // Start point of region, must be same as end point
1536
1537 for( RR_EDGE& rr_edge: rr_outline )
1538 {
1539 if( aCornerRadius ) // Guard: ensure we do not create arcs with radius = 0
1540 {
1541 // LineTo( rr_edge.m_end ); // made in plotArc()
1542 plotArc( rr_edge.m_center, -rr_edge.m_arc_angle_start,
1543 -rr_edge.m_arc_angle_start + ANGLE_90, aCornerRadius, true );
1544 }
1545 else
1546 {
1547 LineTo( rr_edge.m_end );
1548 }
1549 }
1550
1551 fmt::println( m_outputFile, "G37*" ); // Close region
1552}
1553
1554
1555void GERBER_PLOTTER::FlashPadCustom( const VECTOR2I& aPadPos, const VECTOR2I& aSize,
1556 const EDA_ANGLE& aOrient, SHAPE_POLY_SET* aPolygons,
1557 void* aData )
1558{
1559 // A Pad custom is plotted as polygon (a region in Gerber language).
1560 GBR_METADATA gbr_metadata;
1561
1562 if( aData )
1563 gbr_metadata = *static_cast<GBR_METADATA*>( aData );
1564
1565 SHAPE_POLY_SET polyshape = aPolygons->CloneDropTriangulation();
1566
1567 std::vector<VECTOR2I> cornerList;
1568
1569 for( int cnt = 0; cnt < polyshape.OutlineCount(); ++cnt )
1570 {
1571 SHAPE_LINE_CHAIN& poly = polyshape.Outline( cnt );
1572
1573 cornerList.clear();
1574
1575 for( int ii = 0; ii < poly.PointCount(); ++ii )
1576 cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
1577
1578 // Close polygon
1579 cornerList.push_back( cornerList[0] );
1580
1581#ifdef GBR_USE_MACROS_FOR_CUSTOM_PAD
1583 {
1584 PlotGerberRegion( cornerList, &gbr_metadata );
1585 }
1586 else
1587 {
1588 // An AM will be created. the shape must be in position 0,0 and orientation 0
1589 // to be able to reuse the same AM for pads having the same shape
1590 for( size_t ii = 0; ii < cornerList.size(); ii++ )
1591 {
1592 cornerList[ii] -= aPadPos;
1593 RotatePoint( cornerList[ii], -aOrient );
1594 }
1595
1596 VECTOR2D pos_dev = userToDeviceCoordinates( aPadPos );
1597 selectAperture( cornerList, aOrient, APERTURE::AM_FREE_POLYGON,
1598 gbr_metadata.GetApertureAttrib(),
1599 gbr_metadata.GetCustomAttribute() );
1600 formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1601
1602 emitDcode( pos_dev, 3 );
1603 }
1604#else
1605 PlotGerberRegion( cornerList, &gbr_metadata );
1606#endif
1607 }
1608}
1609
1610
1611void GERBER_PLOTTER::FlashPadChamferRoundRect( const VECTOR2I& aShapePos, const VECTOR2I& aPadSize,
1612 int aCornerRadius, double aChamferRatio,
1613 int aChamferPositions, const EDA_ANGLE& aPadOrient,
1614 void* aData )
1615{
1616 GBR_METADATA gbr_metadata;
1617
1618 if( aData )
1619 gbr_metadata = *static_cast<GBR_METADATA*>( aData );
1620
1621 VECTOR2D pos_device = userToDeviceCoordinates( aShapePos );
1622 SHAPE_POLY_SET outline;
1623 std::vector<VECTOR2I> cornerList;
1624
1625 bool hasRoundedCorner = aCornerRadius != 0 && aChamferPositions != 15;
1626
1627#ifdef GBR_USE_MACROS_FOR_CHAMFERED_RECT
1628 // Round rect shape or Apert Macros disabled
1629 if( hasRoundedCorner || m_gerberDisableApertMacros )
1630#endif
1631 {
1632 TransformRoundChamferedRectToPolygon( outline, aShapePos, aPadSize, aPadOrient,
1633 aCornerRadius, aChamferRatio, aChamferPositions, 0,
1635
1636 // Build the corner list
1637 const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
1638
1639 for( int ii = 0; ii < corners.PointCount(); ii++ )
1640 cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
1641
1642 // Close the polygon
1643 cornerList.push_back( cornerList[0] );
1644
1645#ifdef GBR_USE_MACROS_FOR_CHAMFERED_ROUND_RECT
1647 {
1648 PlotGerberRegion( cornerList, &gbr_metadata );
1649 }
1650 else
1651 {
1652 // An AM will be created. the shape must be in position 0,0 and orientation 0
1653 // to be able to reuse the same AM for pads having the same shape
1654 for( size_t ii = 0; ii < cornerList.size(); ii++ )
1655 {
1656 cornerList[ii] -= aShapePos;
1657 RotatePoint( cornerList[ii], -aPadOrient );
1658 }
1659
1660 selectAperture( cornerList, aPadOrient, APERTURE::AM_FREE_POLYGON,
1661 gbr_metadata.GetApertureAttrib(),
1662 gbr_metadata.GetCustomAttribute() );
1663 formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1664
1665 emitDcode( pos_device, 3 );
1666 }
1667#else
1668 PlotGerberRegion( cornerList, &gbr_metadata );
1669#endif
1670
1671 return;
1672 }
1673
1674 // Build the chamfered polygon (4 to 8 corners )
1675 TransformRoundChamferedRectToPolygon( outline, VECTOR2I( 0, 0 ), aPadSize, ANGLE_0, 0,
1676 aChamferRatio, aChamferPositions, 0,
1678
1679 // Build the corner list
1680 const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
1681
1682 // Generate the polygon (4 to 8 corners )
1683 for( int ii = 0; ii < corners.PointCount(); ii++ )
1684 cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
1685
1686 switch( cornerList.size() )
1687 {
1688 case 4:
1690 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE4P,
1691 gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
1692 break;
1693
1694 case 5:
1696 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE5P,
1697 gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
1698 break;
1699
1700 case 6:
1702 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE6P,
1703 gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
1704 break;
1705
1706 case 7:
1708 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE7P,
1709 gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
1710 break;
1711
1712 case 8:
1714 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE8P,
1715 gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
1716 break;
1717
1718 default:
1719 wxLogMessage( wxS( "FlashPadChamferRoundRect(): Unexpected number of corners (%d)" ),
1720 (int)cornerList.size() );
1721 break;
1722 }
1723
1724 formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1725
1726 emitDcode( pos_device, 3 );
1727}
1728
1729
1730void GERBER_PLOTTER::FlashPadTrapez( const VECTOR2I& aPadPos, const VECTOR2I* aCorners,
1731 const EDA_ANGLE& aPadOrient, void* aData )
1732{
1733 // polygon corners list
1734 std::vector<VECTOR2I> cornerList = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
1735
1736 // Draw the polygon and fill the interior as required
1737 for( unsigned ii = 0; ii < 4; ii++ )
1738 {
1739 RotatePoint( cornerList[ii], aPadOrient );
1740 cornerList[ii] += aPadPos;
1741 }
1742
1743 // Close the polygon
1744 cornerList.push_back( cornerList[0] );
1745
1746 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1747 GBR_METADATA metadata;
1748
1749 if( gbr_metadata )
1750 metadata = *gbr_metadata;
1751
1752 // Plot a filled polygon:
1753#ifdef GBR_USE_MACROS_FOR_TRAPEZOID
1755#endif
1756 {
1758 VECTOR2D pos_dev = userToDeviceCoordinates( aPadPos );
1759
1760 // polygon corners list
1761 std::vector<VECTOR2I> corners = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
1762 int aperture_attribute = 0;
1763 std::string custom_attribute = "";
1764
1765 if( gbr_metadata )
1766 {
1767 aperture_attribute = gbr_metadata->GetApertureAttrib();
1768 custom_attribute = gbr_metadata->GetCustomAttribute();
1769 }
1770
1771 selectAperture( corners, aPadOrient, APERTURE::APER_MACRO_OUTLINE4P, aperture_attribute,
1772 custom_attribute );
1773
1774 if( gbr_metadata )
1775 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1776
1777 emitDcode( pos_dev, 3 );
1778 return;
1779 }
1780
1781 PlotGerberRegion( cornerList, &metadata );
1782}
1783
1784
1785void GERBER_PLOTTER::FlashRegularPolygon( const VECTOR2I& aShapePos, int aDiameter,
1786 int aCornerCount, const EDA_ANGLE& aOrient,
1787 void* aData )
1788{
1789 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1790
1791 GBR_METADATA metadata;
1792
1793 if( gbr_metadata )
1794 metadata = *gbr_metadata;
1795
1796 APERTURE::APERTURE_TYPE apert_type =
1797 ( APERTURE::APERTURE_TYPE )( APERTURE::AT_REGULAR_POLY3 + aCornerCount - 3 );
1798
1799 wxASSERT( apert_type >= APERTURE::APERTURE_TYPE::AT_REGULAR_POLY3
1801
1802 selectApertureWithAttributes( aShapePos, gbr_metadata, VECTOR2I( 0, 0 ), aDiameter / 2,
1803 aOrient, apert_type );
1804}
1805
1806
1808 const COLOR4D& aColor,
1809 const wxString& aText,
1810 const EDA_ANGLE& aOrient,
1811 const VECTOR2I& aSize,
1812 enum GR_TEXT_H_ALIGN_T aH_justify,
1813 enum GR_TEXT_V_ALIGN_T aV_justify,
1814 int aWidth,
1815 bool aItalic,
1816 bool aBold,
1817 bool aMultilineAllowed,
1818 KIFONT::FONT* aFont,
1819 const KIFONT::METRICS& aFontMetrics,
1820 void* aData )
1821{
1822 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1823
1824 if( gbr_metadata )
1825 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1826
1827 PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth,
1828 aItalic, aBold, aMultilineAllowed, aFont, aFontMetrics, aData );
1829}
1830
1831
1833 const COLOR4D& aColor,
1834 const wxString& aText,
1835 const TEXT_ATTRIBUTES& aAttributes,
1836 KIFONT::FONT* aFont,
1837 const KIFONT::METRICS& aFontMetrics,
1838 void* aData )
1839{
1840 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1841
1842 if( gbr_metadata )
1843 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1844
1845 PLOTTER::PlotText( aPos, aColor, aText, aAttributes, aFont, aFontMetrics, aData );
1846}
1847
1848
1850{
1851 if( aPositive )
1852 fmt::println( m_outputFile, "%LPD*%" );
1853 else
1854 fmt::println( m_outputFile, "%LPC*%" );
1855}
1856
1857
1858bool APER_MACRO_FREEPOLY::IsSamePoly( const std::vector<VECTOR2I>& aPolygon ) const
1859{
1860 return polyCompare( m_Corners, aPolygon );
1861}
1862
1863
1864void APER_MACRO_FREEPOLY::Format( FILE * aOutput, double aIu2GbrMacroUnit )
1865{
1866 // Write aperture header
1867 fmt::println( aOutput, "%AM{}{}*", AM_FREEPOLY_BASENAME, m_Id );
1868 fmt::print( aOutput, "4,1,{},", (int) m_Corners.size() );
1869
1870 // Insert a newline after curr_line_count_max coordinates.
1871 int curr_line_corner_count = 0;
1872 const int curr_line_count_max = 20; // <= 0 to disable newlines
1873
1874 for( size_t ii = 0; ii <= m_Corners.size(); ii++ )
1875 {
1876 int jj = ii;
1877
1878 if( ii >= m_Corners.size() )
1879 jj = 0;
1880
1881 // Note: parameter values are always mm or inches
1882 fmt::print( aOutput, "{:#f},{:#f},",
1883 m_Corners[jj].x * aIu2GbrMacroUnit, -m_Corners[jj].y * aIu2GbrMacroUnit );
1884
1885 if( curr_line_count_max >= 0 && ++curr_line_corner_count >= curr_line_count_max )
1886 {
1887 fmt::println( aOutput, "" );
1888 curr_line_corner_count = 0;
1889 }
1890 }
1891
1892 // output rotation parameter
1893 fmt::println( aOutput, "$1*%" );
1894}
1895
1896
1897void APER_MACRO_FREEPOLY_LIST::Format( FILE * aOutput, double aIu2GbrMacroUnit )
1898{
1899 for( int idx = 0; idx < AmCount(); idx++ )
1900 m_AMList[idx].Format( aOutput, aIu2GbrMacroUnit );
1901}
1902
1903
1904void APER_MACRO_FREEPOLY_LIST::Append( const std::vector<VECTOR2I>& aPolygon )
1905{
1906 m_AMList.emplace_back( aPolygon, AmCount() );
1907}
1908
1909
1910int APER_MACRO_FREEPOLY_LIST::FindAm( const std::vector<VECTOR2I>& aPolygon ) const
1911{
1912 for( int idx = 0; idx < AmCount(); idx++ )
1913 {
1914 if( m_AMList[idx].IsSamePoly( aPolygon ) )
1915 return idx;
1916 }
1917
1918 return -1;
1919}
#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:98
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:105
wxArrayString m_headerExtraLines
Definition plotter.h:724
virtual void ThickPoly(const SHAPE_POLY_SET &aPoly, int aWidth, void *aData)
Definition plotter.cpp:625
bool m_plotMirror
Definition plotter.h:701
static const int USE_DEFAULT_LINE_WIDTH
Definition plotter.h:141
void MoveTo(const VECTOR2I &pos)
Definition plotter.h:309
void FinishTo(const VECTOR2I &pos)
Definition plotter.h:319
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:183
double m_iuPerDeviceUnit
Definition plotter.h:698
VECTOR2I m_plotOffset
Definition plotter.h:700
virtual VECTOR2D userToDeviceCoordinates(const VECTOR2I &aCoordinate)
Modify coordinates according to the orientation, scale factor, and offsets trace.
Definition plotter.cpp:93
VECTOR2I m_paperSize
Definition plotter.h:722
virtual void ThickArc(const EDA_SHAPE &aArcShape, void *aData, int aWidth)
Definition plotter.cpp:582
int GetPlotterArcHighDef() const
Definition plotter.h:275
char m_penState
Definition plotter.h:713
wxString m_creator
Definition plotter.h:716
int m_currentPenWidth
Definition plotter.h:712
double m_plotScale
Plot scale - chosen by the user (even implicitly with 'fit in a4')
Definition plotter.h:690
FILE * m_outputFile
Output file.
Definition plotter.h:707
void LineTo(const VECTOR2I &pos)
Definition plotter.h:314
void PenFinish()
Definition plotter.h:325
static const int DO_NOT_SET_LINE_WIDTH
Definition plotter.h:140
virtual void ThickSegment(const VECTOR2I &start, const VECTOR2I &end, int width, void *aData)
Definition plotter.cpp:540
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:716
virtual void ThickRect(const VECTOR2I &p1, const VECTOR2I &p2, int width, void *aData)
Definition plotter.cpp:607
RENDER_SETTINGS * m_renderSettings
Definition plotter.h:726
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:646
double m_IUsPerDecimil
Definition plotter.h:696
virtual void ThickCircle(const VECTOR2I &pos, int diametre, int width, void *aData)
Definition plotter.cpp:613
virtual int GetCurrentLineWidth() const
Definition plotter.h:183
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:63
@ NO_FILL
Definition eda_shape.h:64
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:65
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:687
VECTOR2< double > VECTOR2D
Definition vector2d.h:686