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 {
855 BOX2I box( p1, VECTOR2I( p2.x - p1.x, p2.y - p1.y ) );
856 box.Normalize();
857 SHAPE_RECT rect( box );
858 rect.SetRadius( aCornerRadius );
859 PlotPoly( rect.Outline(), fill, width, nullptr );
860 return;
861 }
862
863 std::vector<VECTOR2I> cornerList;
864
865 cornerList.reserve( 5 );
866
867 // Build corners list
868 cornerList.push_back( p1 );
869
870 VECTOR2I corner( p1.x, p2.y );
871 cornerList.push_back( corner );
872 cornerList.push_back( p2 );
873 corner.x = p2.x;
874 corner.y = p1.y;
875 cornerList.push_back( corner );
876 cornerList.push_back( p1 );
877
878 PlotPoly( cornerList, fill, width, nullptr );
879}
880
881
882void GERBER_PLOTTER::Circle( const VECTOR2I& aCenter, int aDiameter, FILL_T aFill, int aWidth )
883{
884 Arc( aCenter, ANGLE_0, ANGLE_180, aDiameter / 2, aFill, aWidth );
885 Arc( aCenter, ANGLE_180, ANGLE_180, aDiameter / 2, aFill, aWidth );
886}
887
888
889
890void GERBER_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
891 const EDA_ANGLE& aAngle, double aRadius, FILL_T aFill, int aWidth )
892{
893 SetCurrentLineWidth( aWidth );
894
895 double arcLength = std::abs( aRadius * aAngle.AsRadians() );
896
897 if( arcLength < 100 || std::abs( aAngle.AsDegrees() ) < 0.1 )
898 {
899 // Prevent plotting very short arcs as full circles, especially with 4.5 mm precision.
900 // Also reduce the risk of integer overflow issues.
901 polyArc( aCenter, aStartAngle, aAngle, aRadius, aFill, GetCurrentLineWidth() );
902 }
903 else
904 {
905 EDA_ANGLE endAngle = aStartAngle + aAngle;
906
907 // aFill is not used here.
908 plotArc( aCenter, aStartAngle, endAngle, aRadius, false );
909 }
910}
911
912
913void GERBER_PLOTTER::plotArc( const SHAPE_ARC& aArc, bool aPlotInRegion )
914{
915 VECTOR2I start( aArc.GetP0() );
916 VECTOR2I end( aArc.GetP1() );
917 VECTOR2I center( aArc.GetCenter() );
918
919 if( !aPlotInRegion )
920 MoveTo( start);
921 else
922 LineTo( start );
923
925
926 // devRelCenter is the position on arc center relative to the arc start, in Gerber coord.
927 // Warning: it is **not** userToDeviceCoordinates( center - start ) when the plotter
928 // has an offset.
930
931 // We need to know if the arc is CW or CCW in device coordinates, so build this arc.
932 SHAPE_ARC deviceArc( userToDeviceCoordinates( start ),
934 devEnd, 0 );
935
936 fmt::println( m_outputFile, "G75*" ); // Multiquadrant (360 degrees) mode
937
938 if( deviceArc.IsClockwise() )
939 fmt::println( m_outputFile, "G02*" ); // Active circular interpolation, CW
940 else
941 fmt::println( m_outputFile, "G03*" ); // Active circular interpolation, CCW
942
943 fmt::println( m_outputFile, "X{}Y{}I{}J{}D01*",
944 KiROUND( devEnd.x ), KiROUND( devEnd.y ),
945 KiROUND( devRelCenter.x ), KiROUND( devRelCenter.y ) );
946
947 fmt::println( m_outputFile, "G01*" ); // Back to linear interpolate (perhaps useless here).
948}
949
950
951void GERBER_PLOTTER::plotArc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle,
952 const EDA_ANGLE& aEndAngle, double aRadius, bool aPlotInRegion )
953{
954 VECTOR2I start, end;
955 start.x = KiROUND( aCenter.x + aRadius * aStartAngle.Cos() );
956 start.y = KiROUND( aCenter.y + aRadius * aStartAngle.Sin() );
957
958 if( !aPlotInRegion )
959 MoveTo( start );
960 else
961 LineTo( start );
962
963 end.x = KiROUND( aCenter.x + aRadius * aEndAngle.Cos() );
964 end.y = KiROUND( aCenter.y + aRadius * aEndAngle.Sin() );
966
967 // devRelCenter is the position on arc center relative to the arc start, in Gerber coord.
968 VECTOR2D devRelCenter = userToDeviceCoordinates( aCenter ) - userToDeviceCoordinates( start );
969
970 fmt::println( m_outputFile, "G75*" ); // Multiquadrant (360 degrees) mode
971
972 if( aStartAngle > aEndAngle )
973 fmt::println( m_outputFile, "G03*" ); // Active circular interpolation, CCW
974 else
975 fmt::println( m_outputFile, "G02*" ); // Active circular interpolation, CW
976
977 fmt::println( m_outputFile, "X{}Y{}I{}J{}D01*",
978 KiROUND( devEnd.x ), KiROUND( devEnd.y ),
979 KiROUND( devRelCenter.x ), KiROUND( devRelCenter.y ) );
980
981 fmt::println( m_outputFile, "G01*" ); // Back to linear interpolate (perhaps useless here).
982}
983
984
986{
987 if( aPoly.PointCount() <= 2 )
988 return;
989
990 bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
991
992 if( aGbrMetadata )
993 {
994 std::string attrib = aGbrMetadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
995
996 if( !attrib.empty() )
997 {
998 fmt::print( m_outputFile, "{}", attrib );
999 clearTA_AperFunction = true;
1000 }
1001 }
1002
1003 PlotPoly( aPoly, FILL_T::FILLED_SHAPE, 0 , aGbrMetadata );
1004
1005 // Clear the TA attribute, to avoid the next item to inherit it:
1006 if( clearTA_AperFunction )
1007 {
1008 if( m_useX2format )
1009 fmt::println( m_outputFile, "%TD.AperFunction*%" );
1010 else
1011 fmt::println( m_outputFile, "G04 #@! TD.AperFunction*" );
1012 }
1013}
1014
1015
1016void GERBER_PLOTTER::PlotGerberRegion( const std::vector<VECTOR2I>& aCornerList,
1017 GBR_METADATA* aGbrMetadata )
1018{
1019 if( aCornerList.size() <= 2 )
1020 return;
1021
1022 bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
1023
1024 if( aGbrMetadata )
1025 {
1026 std::string attrib = aGbrMetadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
1027
1028 if( !attrib.empty() )
1029 {
1030 fmt::print( m_outputFile, "{}", attrib );
1031 clearTA_AperFunction = true;
1032 }
1033 }
1034
1035 PlotPoly( aCornerList, FILL_T::FILLED_SHAPE, 0, aGbrMetadata );
1036
1037 // Clear the TA attribute, to avoid the next item to inherit it:
1038 if( clearTA_AperFunction )
1039 {
1040 if( m_useX2format )
1041 fmt::println( m_outputFile, "%TD.AperFunction*%" );
1042 else
1043 fmt::println( m_outputFile, "G04 #@! TD.AperFunction*" );
1044 }
1045}
1046
1047
1049 int aWidth, GBR_METADATA* aGbrMetadata )
1050{
1051 // plot a filled polygon using Gerber region, therefore adding X2 attributes
1052 // to the solid polygon
1053 if( aWidth || aFill == FILL_T::NO_FILL )
1054 PlotPoly( aPoly, FILL_T::NO_FILL, aWidth, aGbrMetadata );
1055
1056 if( aFill != FILL_T::NO_FILL )
1057 PlotGerberRegion( aPoly, aGbrMetadata );
1058}
1059
1060
1061void GERBER_PLOTTER::PlotPoly( const SHAPE_LINE_CHAIN& aPoly, FILL_T aFill, int aWidth,
1062 void* aData )
1063{
1064 if( aPoly.CPoints().size() <= 1 )
1065 return;
1066
1067 // Gerber format does not know filled polygons with thick outline
1068 // Therefore, to plot a filled polygon with outline having a thickness,
1069 // one should plot outline as thick segments
1070 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1071
1072 if( gbr_metadata )
1073 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1074
1075 if( aFill != FILL_T::NO_FILL )
1076 {
1077 fmt::println( m_outputFile, "G36*" );
1078
1079 MoveTo( VECTOR2I( aPoly.CPoint( 0 ) ) );
1080
1081 fmt::println( m_outputFile, "G01*" ); // Set linear interpolation.
1082
1083 for( int ii = 1; ii < aPoly.PointCount(); ii++ )
1084 {
1085 int arcindex = aPoly.ArcIndex( ii );
1086
1087 if( arcindex < 0 )
1088 {
1090 LineTo( VECTOR2I( aPoly.CPoint( ii ) ) );
1091 }
1092 else
1093 {
1094 const SHAPE_ARC& arc = aPoly.Arc( arcindex );
1095
1096 plotArc( arc, true );
1097
1098 // skip points on arcs, since we plot the arc itself
1099 while( ii+1 < aPoly.PointCount() && arcindex == aPoly.ArcIndex( ii+1 ) )
1100 ii++;
1101 }
1102 }
1103
1104 // If the polygon is not closed, close it:
1105 if( aPoly.CPoint( 0 ) != aPoly.CLastPoint() )
1106 FinishTo( VECTOR2I( aPoly.CPoint( 0 ) ) );
1107
1108 fmt::println( m_outputFile, "G37*" );
1109 }
1110 else if( aWidth != 0 ) // Draw the polyline/polygon outline
1111 {
1112 SetCurrentLineWidth( aWidth, gbr_metadata );
1113
1114 MoveTo( VECTOR2I( aPoly.CPoint( 0 ) ) );
1115
1116 for( int ii = 1; ii < aPoly.PointCount(); ii++ )
1117 {
1118 int arcindex = aPoly.ArcIndex( ii );
1119
1120 if( arcindex < 0 )
1121 {
1123 LineTo( VECTOR2I( aPoly.CPoint( ii ) ) );
1124 }
1125 else
1126 {
1127 const SHAPE_ARC& arc = aPoly.Arc( arcindex );
1128
1129 plotArc( arc, true );
1130
1131 // skip points on arcs, since we plot the arc itself
1132 while( ii+1 < aPoly.PointCount() && arcindex == aPoly.ArcIndex( ii+1 ) )
1133 ii++;
1134 }
1135 }
1136
1137 // Ensure the thick outline is closed for filled polygons
1138 // (if not filled, could be only a polyline)
1139 if( ( aPoly.CPoint( 0 ) != aPoly.CLastPoint() ) && ( aPoly.IsClosed() || aFill != FILL_T::NO_FILL ) )
1140 LineTo( VECTOR2I( aPoly.CPoint( 0 ) ) );
1141
1142 PenFinish();
1143 }
1144}
1145
1146
1147void GERBER_PLOTTER::PlotPoly( const std::vector<VECTOR2I>& aCornerList, FILL_T aFill, int aWidth,
1148 void * aData )
1149{
1150 if( aCornerList.size() <= 1 )
1151 return;
1152
1153 // Gerber format does not know filled polygons with thick outline
1154 // Therefore, to plot a filled polygon with outline having a thickness,
1155 // one should plot outline as thick segments
1156 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1157
1158 if( gbr_metadata )
1159 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1160
1161 if( aFill != FILL_T::NO_FILL )
1162 {
1163 fmt::println( m_outputFile, "G36*" );
1164
1165 MoveTo( aCornerList[0] );
1166 fmt::println( m_outputFile, "G01*" ); // Set linear interpolation.
1167
1168 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1169 LineTo( aCornerList[ii] );
1170
1171 // If the polygon is not closed, close it:
1172 if( aCornerList[0] != aCornerList[aCornerList.size()-1] )
1173 FinishTo( aCornerList[0] );
1174
1175 fmt::println( m_outputFile, "G37*" );
1176 }
1177
1178 if( aWidth != 0 || aFill == FILL_T::NO_FILL ) // Draw the polyline/polygon outline
1179 {
1180 SetCurrentLineWidth( aWidth, gbr_metadata );
1181
1182 MoveTo( aCornerList[0] );
1183
1184 for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1185 LineTo( aCornerList[ii] );
1186
1187 // Ensure the thick outline is closed for filled polygons
1188 // (if not filled, could be only a polyline)
1189 if( aFill != FILL_T::NO_FILL && ( aCornerList[aCornerList.size() - 1] != aCornerList[0] ) )
1190 LineTo( aCornerList[0] );
1191
1192 PenFinish();
1193 }
1194}
1195
1196
1197void GERBER_PLOTTER::ThickSegment( const VECTOR2I& start, const VECTOR2I& end, int width, void* aData )
1198{
1199 if( GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData ) )
1200 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1201
1202 SetCurrentLineWidth( width, aData );
1204}
1205
1206
1207void GERBER_PLOTTER::ThickArc( const VECTOR2D& aCentre, const EDA_ANGLE& aStartAngle,
1208 const EDA_ANGLE& aAngle, double aRadius, int aWidth, void* aData )
1209{
1210 if( GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData ) )
1211 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1212
1213 SetCurrentLineWidth( aWidth, aData );
1214 PLOTTER::ThickArc( aCentre, aStartAngle, aAngle, aRadius, DO_NOT_SET_LINE_WIDTH, aData );
1215}
1216
1217
1218void GERBER_PLOTTER::ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width, void* aData )
1219{
1220 if( GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData ) )
1221 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1222
1223 SetCurrentLineWidth( width, aData );
1224 PLOTTER::ThickRect( p1, p2, DO_NOT_SET_LINE_WIDTH, aData );
1225}
1226
1227
1228void GERBER_PLOTTER::ThickCircle( const VECTOR2I& pos, int diametre, int width, void* aData )
1229{
1230 if( GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData ) )
1231 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1232
1233 SetCurrentLineWidth( width, aData );
1234 PLOTTER::ThickCircle( pos, diametre, DO_NOT_SET_LINE_WIDTH, aData );
1235}
1236
1237
1238void GERBER_PLOTTER::FilledCircle( const VECTOR2I& pos, int diametre, void* aData )
1239{
1240 // A filled circle is a graphic item, not a pad.
1241 // So it is drawn, not flashed.
1242 if( GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData ) )
1243 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1244
1245 // Draw a circle of diameter = diameter/2 with a line thickness = radius,
1246 // To create a filled circle
1247 SetCurrentLineWidth( diametre/2, aData );
1248 Circle( pos, diametre/2, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1249}
1250
1251
1252void GERBER_PLOTTER::ThickPoly( const SHAPE_POLY_SET& aPoly, int aWidth, void* aData )
1253{
1254 if( GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData ) )
1255 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1256
1257 SetCurrentLineWidth( aWidth, aData );
1259}
1260
1261
1262void GERBER_PLOTTER::FlashPadCircle( const VECTOR2I& pos, int diametre, void* aData )
1263{
1264 VECTOR2I size( diametre, diametre );
1265 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1266
1267 selectApertureWithAttributes( pos, gbr_metadata, size, 0, ANGLE_0, APERTURE::AT_CIRCLE );
1268}
1269
1270
1271void GERBER_PLOTTER::FlashPadOval( const VECTOR2I& aPos, const VECTOR2I& aSize,
1272 const EDA_ANGLE& aOrient, void* aData )
1273{
1274 wxASSERT( m_outputFile );
1275
1276 VECTOR2I size( aSize );
1277 EDA_ANGLE orient( aOrient );
1278 orient.Normalize();
1279 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1280
1281 // Flash a vertical or horizontal shape (this is a basic aperture).
1282 if( orient.IsCardinal() )
1283 {
1284 if( orient.IsCardinal90() )
1285 std::swap( size.x, size.y );
1286
1287 selectApertureWithAttributes( aPos, gbr_metadata, size, 0, ANGLE_0, APERTURE::AT_OVAL );
1288 }
1289 else // Plot pad as region.
1290 // Only regions and flashed items accept a object attribute TO.P for the pin name
1291 {
1292#ifdef GBR_USE_MACROS_FOR_ROTATED_OVAL
1294#endif
1295 {
1296 m_hasApertureRotOval = true;
1297 // We are using a aperture macro that expect size.y < size.x
1298 // i.e draw a horizontal line for rotation = 0.0
1299 // size.x = length, size.y = width
1300 if( size.x < size.y )
1301 {
1302 std::swap( size.x, size.y );
1303 orient += ANGLE_90;
1304
1305 if( orient > ANGLE_180 )
1306 orient -= ANGLE_180;
1307 }
1308
1309 selectApertureWithAttributes( aPos, gbr_metadata, size, 0, orient,
1311 return;
1312 }
1313
1314 // Draw the oval as round rect pad with a radius = 50% min size)
1315 // In gerber file, it will be drawn as a region with arcs, and can be
1316 // detected as pads (similar to a flashed pad)
1317 FlashPadRoundRect( aPos, aSize, std::min( aSize.x, aSize.y ) / 2, orient, aData );
1318 }
1319}
1320
1321
1322void GERBER_PLOTTER::FlashPadRect( const VECTOR2I& pos, const VECTOR2I& aSize,
1323 const EDA_ANGLE& aOrient, void* aData )
1324{
1325 wxASSERT( m_outputFile );
1326
1327 VECTOR2I size( aSize );
1328 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1329
1330 // Horizontal / vertical rect can use a basic aperture (not a macro)
1331 // so use it for rotation n*90 deg
1332 if( aOrient.IsCardinal() )
1333 {
1334 // Build the not rotated equivalent shape:
1335 if( aOrient.IsCardinal90() )
1336 std::swap( size.x, size.y );
1337
1338 selectApertureWithAttributes( pos, gbr_metadata, size, 0, ANGLE_0, APERTURE::AT_RECT );
1339 }
1340 else
1341 {
1342#ifdef GBR_USE_MACROS_FOR_ROTATED_RECT
1344 {
1345 m_hasApertureRotRect = true;
1346
1347 selectApertureWithAttributes( pos, gbr_metadata, size, 0, aOrient,
1349 }
1350 else
1351#endif
1352 {
1353 // plot pad shape as Gerber region
1354 VECTOR2I coord[4];
1355
1356 // coord[0] is assumed the lower left
1357 // coord[1] is assumed the upper left
1358 // coord[2] is assumed the upper right
1359 // coord[3] is assumed the lower right
1360 coord[0].x = -size.x/2; // lower left
1361 coord[0].y = size.y/2;
1362 coord[1].x = -size.x/2; // upper left
1363 coord[1].y = -size.y/2;
1364 coord[2].x = size.x/2; // upper right
1365 coord[2].y = -size.y/2;
1366 coord[3].x = size.x/2; // lower right
1367 coord[3].y = size.y/2;
1368
1369 FlashPadTrapez( pos, coord, aOrient, aData );
1370 }
1371 }
1372}
1373
1374
1375void GERBER_PLOTTER::FlashPadRoundRect( const VECTOR2I& aPadPos, const VECTOR2I& aSize,
1376 int aCornerRadius, const EDA_ANGLE& aOrient, void* aData )
1377
1378{
1379 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1380
1381#ifdef GBR_USE_MACROS_FOR_ROUNDRECT
1383#endif
1384 {
1386
1387 selectApertureWithAttributes( aPadPos, gbr_metadata, aSize, aCornerRadius, aOrient,
1389 return;
1390 }
1391
1392 // A Pad RoundRect is plotted as a Gerber region.
1393 // Initialize region metadata:
1394 bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
1395
1396 if( gbr_metadata )
1397 {
1398 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1399 std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
1400
1401 if( !attrib.empty() )
1402 {
1403 fmt::print( m_outputFile, "{}", attrib );
1404 clearTA_AperFunction = true;
1405 }
1406 }
1407
1408 // Plot the region using arcs in corners.
1409 plotRoundRectAsRegion( aPadPos, aSize, aCornerRadius, aOrient );
1410
1411 // Clear the TA attribute, to avoid the next item to inherit it:
1412 if( clearTA_AperFunction )
1413 {
1414 if( m_useX2format )
1415 fmt::println( m_outputFile, "%TD.AperFunction*%" );
1416 else
1417 fmt::println( m_outputFile, "G04 #@! TD.AperFunction*" );
1418 }
1419}
1420
1421
1422void GERBER_PLOTTER::plotRoundRectAsRegion( const VECTOR2I& aRectCenter, const VECTOR2I& aSize,
1423 int aCornerRadius, const EDA_ANGLE& aOrient )
1424{
1425 // The region outline is generated by 4 sides and 4 90 deg arcs
1426 // 1 --- 2
1427 // | c |
1428 // 4 --- 3
1429
1430 // Note also in user coordinates the Y axis is from top to bottom
1431 // for historical reasons.
1432
1433 // A helper structure to handle outlines coordinates (segments and arcs)
1434 // in user coordinates
1435 struct RR_EDGE
1436 {
1437 VECTOR2I m_start;
1438 VECTOR2I m_end;
1439 VECTOR2I m_center;
1440 EDA_ANGLE m_arc_angle_start;
1441 };
1442
1443 int hsizeX = aSize.x/2;
1444 int hsizeY = aSize.y/2;
1445
1446 RR_EDGE curr_edge;
1447 std::vector<RR_EDGE> rr_outline;
1448
1449 rr_outline.reserve( 4 );
1450
1451 // Build outline coordinates, relative to rectangle center, rotation 0:
1452
1453 // Top left corner 1 (and 4 to 1 left vertical side @ x=-hsizeX)
1454 curr_edge.m_start.x = -hsizeX;
1455 curr_edge.m_start.y = hsizeY - aCornerRadius;
1456 curr_edge.m_end.x = curr_edge.m_start.x;
1457 curr_edge.m_end.y = -hsizeY + aCornerRadius;
1458 curr_edge.m_center.x = -hsizeX + aCornerRadius;
1459 curr_edge.m_center.y = curr_edge.m_end.y;
1460 curr_edge.m_arc_angle_start = aOrient + ANGLE_180;
1461
1462 rr_outline.push_back( curr_edge );
1463
1464 // Top right corner 2 (and 1 to 2 top horizontal side @ y=-hsizeY)
1465 curr_edge.m_start.x = -hsizeX + aCornerRadius;
1466 curr_edge.m_start.y = -hsizeY;
1467 curr_edge.m_end.x = hsizeX - aCornerRadius;
1468 curr_edge.m_end.y = curr_edge.m_start.y;
1469 curr_edge.m_center.x = curr_edge.m_end.x;
1470 curr_edge.m_center.y = -hsizeY + aCornerRadius;
1471 curr_edge.m_arc_angle_start = aOrient + ANGLE_90;
1472
1473 rr_outline.push_back( curr_edge );
1474
1475 // bottom right corner 3 (and 2 to 3 right vertical side @ x=hsizeX)
1476 curr_edge.m_start.x = hsizeX;
1477 curr_edge.m_start.y = -hsizeY + aCornerRadius;
1478 curr_edge.m_end.x = curr_edge.m_start.x;
1479 curr_edge.m_end.y = hsizeY - aCornerRadius;
1480 curr_edge.m_center.x = hsizeX - aCornerRadius;
1481 curr_edge.m_center.y = curr_edge.m_end.y;
1482 curr_edge.m_arc_angle_start = aOrient + ANGLE_0;
1483
1484 rr_outline.push_back( curr_edge );
1485
1486 // bottom left corner 4 (and 3 to 4 bottom horizontal side @ y=hsizeY)
1487 curr_edge.m_start.x = hsizeX - aCornerRadius;
1488 curr_edge.m_start.y = hsizeY;
1489 curr_edge.m_end.x = -hsizeX + aCornerRadius;
1490 curr_edge.m_end.y = curr_edge.m_start.y;
1491 curr_edge.m_center.x = curr_edge.m_end.x;
1492 curr_edge.m_center.y = hsizeY - aCornerRadius;
1493 curr_edge.m_arc_angle_start = aOrient - ANGLE_90;
1494
1495 rr_outline.push_back( curr_edge );
1496
1497 // Move relative coordinates to the actual location and rotation:
1498 VECTOR2I arc_last_center;
1499 EDA_ANGLE arc_last_angle = curr_edge.m_arc_angle_start - ANGLE_90;
1500
1501 for( RR_EDGE& rr_edge: rr_outline )
1502 {
1503 RotatePoint( rr_edge.m_start, aOrient );
1504 RotatePoint( rr_edge.m_end, aOrient );
1505 RotatePoint( rr_edge.m_center, aOrient );
1506 rr_edge.m_start += aRectCenter;
1507 rr_edge.m_end += aRectCenter;
1508 rr_edge.m_center += aRectCenter;
1509 arc_last_center = rr_edge.m_center;
1510 }
1511
1512 // Ensure the region is a closed polygon, i.e. the end point of last segment
1513 // (end of arc) is the same as the first point. Rounding issues can create a
1514 // small difference, mainly for rotated pads.
1515 // calculate last point (end of last arc):
1516 VECTOR2I last_pt;
1517 last_pt.x = arc_last_center.x + KiROUND( aCornerRadius * arc_last_angle.Cos() );
1518 last_pt.y = arc_last_center.y - KiROUND( aCornerRadius * arc_last_angle.Sin() );
1519
1520 VECTOR2I first_pt = rr_outline[0].m_start;
1521
1522#if 0 // For test only:
1523 if( last_pt != first_pt )
1524 wxLogMessage( wxS( "first pt %d %d last pt %d %d" ),
1525 first_pt.x, first_pt.y, last_pt.x, last_pt.y );
1526#endif
1527
1528 fmt::println( m_outputFile, "G36*" ); // Start region
1529 fmt::println( m_outputFile, "G01*" ); // Set linear interpolation.
1530 first_pt = last_pt;
1531 MoveTo( first_pt ); // Start point of region, must be same as end point
1532
1533 for( RR_EDGE& rr_edge: rr_outline )
1534 {
1535 if( aCornerRadius ) // Guard: ensure we do not create arcs with radius = 0
1536 {
1537 // LineTo( rr_edge.m_end ); // made in plotArc()
1538 plotArc( rr_edge.m_center, -rr_edge.m_arc_angle_start,
1539 -rr_edge.m_arc_angle_start + ANGLE_90, aCornerRadius, true );
1540 }
1541 else
1542 {
1543 LineTo( rr_edge.m_end );
1544 }
1545 }
1546
1547 fmt::println( m_outputFile, "G37*" ); // Close region
1548}
1549
1550
1551void GERBER_PLOTTER::FlashPadCustom( const VECTOR2I& aPadPos, const VECTOR2I& aSize,
1552 const EDA_ANGLE& aOrient, SHAPE_POLY_SET* aPolygons,
1553 void* aData )
1554{
1555 // A Pad custom is plotted as polygon (a region in Gerber language).
1556 GBR_METADATA gbr_metadata;
1557
1558 if( aData )
1559 gbr_metadata = *static_cast<GBR_METADATA*>( aData );
1560
1561 SHAPE_POLY_SET polyshape = aPolygons->CloneDropTriangulation();
1562
1563 std::vector<VECTOR2I> cornerList;
1564
1565 for( int cnt = 0; cnt < polyshape.OutlineCount(); ++cnt )
1566 {
1567 SHAPE_LINE_CHAIN& poly = polyshape.Outline( cnt );
1568
1569 cornerList.clear();
1570
1571 for( int ii = 0; ii < poly.PointCount(); ++ii )
1572 cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
1573
1574 // Close polygon
1575 cornerList.push_back( cornerList[0] );
1576
1577#ifdef GBR_USE_MACROS_FOR_CUSTOM_PAD
1579 {
1580 PlotGerberRegion( cornerList, &gbr_metadata );
1581 }
1582 else
1583 {
1584 // An AM will be created. the shape must be in position 0,0 and orientation 0
1585 // to be able to reuse the same AM for pads having the same shape
1586 for( size_t ii = 0; ii < cornerList.size(); ii++ )
1587 {
1588 cornerList[ii] -= aPadPos;
1589 RotatePoint( cornerList[ii], -aOrient );
1590 }
1591
1592 VECTOR2D pos_dev = userToDeviceCoordinates( aPadPos );
1593 selectAperture( cornerList, aOrient, APERTURE::AM_FREE_POLYGON,
1594 gbr_metadata.GetApertureAttrib(),
1595 gbr_metadata.GetCustomAttribute() );
1596 formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1597
1598 emitDcode( pos_dev, 3 );
1599 }
1600#else
1601 PlotGerberRegion( cornerList, &gbr_metadata );
1602#endif
1603 }
1604}
1605
1606
1607void GERBER_PLOTTER::FlashPadChamferRoundRect( const VECTOR2I& aShapePos, const VECTOR2I& aPadSize,
1608 int aCornerRadius, double aChamferRatio,
1609 int aChamferPositions, const EDA_ANGLE& aPadOrient,
1610 void* aData )
1611{
1612 GBR_METADATA gbr_metadata;
1613
1614 if( aData )
1615 gbr_metadata = *static_cast<GBR_METADATA*>( aData );
1616
1617 VECTOR2D pos_device = userToDeviceCoordinates( aShapePos );
1618 SHAPE_POLY_SET outline;
1619 std::vector<VECTOR2I> cornerList;
1620
1621 bool hasRoundedCorner = aCornerRadius != 0 && aChamferPositions != 15;
1622
1623#ifdef GBR_USE_MACROS_FOR_CHAMFERED_RECT
1624 // Round rect shape or Apert Macros disabled
1625 if( hasRoundedCorner || m_gerberDisableApertMacros )
1626#endif
1627 {
1628 TransformRoundChamferedRectToPolygon( outline, aShapePos, aPadSize, aPadOrient,
1629 aCornerRadius, aChamferRatio, aChamferPositions, 0,
1631
1632 // Build the corner list
1633 const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
1634
1635 for( int ii = 0; ii < corners.PointCount(); ii++ )
1636 cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
1637
1638 // Close the polygon
1639 cornerList.push_back( cornerList[0] );
1640
1641#ifdef GBR_USE_MACROS_FOR_CHAMFERED_ROUND_RECT
1643 {
1644 PlotGerberRegion( cornerList, &gbr_metadata );
1645 }
1646 else
1647 {
1648 // An AM will be created. the shape must be in position 0,0 and orientation 0
1649 // to be able to reuse the same AM for pads having the same shape
1650 for( size_t ii = 0; ii < cornerList.size(); ii++ )
1651 {
1652 cornerList[ii] -= aShapePos;
1653 RotatePoint( cornerList[ii], -aPadOrient );
1654 }
1655
1656 selectAperture( cornerList, aPadOrient, APERTURE::AM_FREE_POLYGON,
1657 gbr_metadata.GetApertureAttrib(),
1658 gbr_metadata.GetCustomAttribute() );
1659 formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1660
1661 emitDcode( pos_device, 3 );
1662 }
1663#else
1664 PlotGerberRegion( cornerList, &gbr_metadata );
1665#endif
1666
1667 return;
1668 }
1669
1670 // Build the chamfered polygon (4 to 8 corners )
1671 TransformRoundChamferedRectToPolygon( outline, VECTOR2I( 0, 0 ), aPadSize, ANGLE_0, 0,
1672 aChamferRatio, aChamferPositions, 0,
1674
1675 // Build the corner list
1676 const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
1677
1678 // Generate the polygon (4 to 8 corners )
1679 for( int ii = 0; ii < corners.PointCount(); ii++ )
1680 cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
1681
1682 switch( cornerList.size() )
1683 {
1684 case 4:
1686 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE4P,
1687 gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
1688 break;
1689
1690 case 5:
1692 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE5P,
1693 gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
1694 break;
1695
1696 case 6:
1698 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE6P,
1699 gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
1700 break;
1701
1702 case 7:
1704 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE7P,
1705 gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
1706 break;
1707
1708 case 8:
1710 selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE8P,
1711 gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
1712 break;
1713
1714 default:
1715 wxLogMessage( wxS( "FlashPadChamferRoundRect(): Unexpected number of corners (%d)" ),
1716 (int)cornerList.size() );
1717 break;
1718 }
1719
1720 formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1721
1722 emitDcode( pos_device, 3 );
1723}
1724
1725
1726void GERBER_PLOTTER::FlashPadTrapez( const VECTOR2I& aPadPos, const VECTOR2I* aCorners,
1727 const EDA_ANGLE& aPadOrient, void* aData )
1728{
1729 // polygon corners list
1730 std::vector<VECTOR2I> cornerList = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
1731
1732 // Draw the polygon and fill the interior as required
1733 for( unsigned ii = 0; ii < 4; ii++ )
1734 {
1735 RotatePoint( cornerList[ii], aPadOrient );
1736 cornerList[ii] += aPadPos;
1737 }
1738
1739 // Close the polygon
1740 cornerList.push_back( cornerList[0] );
1741
1742 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1743 GBR_METADATA metadata;
1744
1745 if( gbr_metadata )
1746 metadata = *gbr_metadata;
1747
1748 // Plot a filled polygon:
1749#ifdef GBR_USE_MACROS_FOR_TRAPEZOID
1751#endif
1752 {
1754 VECTOR2D pos_dev = userToDeviceCoordinates( aPadPos );
1755
1756 // polygon corners list
1757 std::vector<VECTOR2I> corners = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
1758 int aperture_attribute = 0;
1759 std::string custom_attribute = "";
1760
1761 if( gbr_metadata )
1762 {
1763 aperture_attribute = gbr_metadata->GetApertureAttrib();
1764 custom_attribute = gbr_metadata->GetCustomAttribute();
1765 }
1766
1767 selectAperture( corners, aPadOrient, APERTURE::APER_MACRO_OUTLINE4P, aperture_attribute,
1768 custom_attribute );
1769
1770 if( gbr_metadata )
1771 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1772
1773 emitDcode( pos_dev, 3 );
1774 return;
1775 }
1776
1777 PlotGerberRegion( cornerList, &metadata );
1778}
1779
1780
1781void GERBER_PLOTTER::FlashRegularPolygon( const VECTOR2I& aShapePos, int aDiameter,
1782 int aCornerCount, const EDA_ANGLE& aOrient,
1783 void* aData )
1784{
1785 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1786
1787 GBR_METADATA metadata;
1788
1789 if( gbr_metadata )
1790 metadata = *gbr_metadata;
1791
1792 APERTURE::APERTURE_TYPE apert_type =
1793 ( APERTURE::APERTURE_TYPE )( APERTURE::AT_REGULAR_POLY3 + aCornerCount - 3 );
1794
1795 wxASSERT( apert_type >= APERTURE::APERTURE_TYPE::AT_REGULAR_POLY3
1797
1798 selectApertureWithAttributes( aShapePos, gbr_metadata, VECTOR2I( 0, 0 ), aDiameter / 2,
1799 aOrient, apert_type );
1800}
1801
1802
1804 const COLOR4D& aColor,
1805 const wxString& aText,
1806 const EDA_ANGLE& aOrient,
1807 const VECTOR2I& aSize,
1808 enum GR_TEXT_H_ALIGN_T aH_justify,
1809 enum GR_TEXT_V_ALIGN_T aV_justify,
1810 int aWidth,
1811 bool aItalic,
1812 bool aBold,
1813 bool aMultilineAllowed,
1814 KIFONT::FONT* aFont,
1815 const KIFONT::METRICS& aFontMetrics,
1816 void* aData )
1817{
1818 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1819
1820 if( gbr_metadata )
1821 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1822
1823 PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth,
1824 aItalic, aBold, aMultilineAllowed, aFont, aFontMetrics, aData );
1825}
1826
1827
1829 const COLOR4D& aColor,
1830 const wxString& aText,
1831 const TEXT_ATTRIBUTES& aAttributes,
1832 KIFONT::FONT* aFont,
1833 const KIFONT::METRICS& aFontMetrics,
1834 void* aData )
1835{
1836 GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1837
1838 if( gbr_metadata )
1839 formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1840
1841 PLOTTER::PlotText( aPos, aColor, aText, aAttributes, aFont, aFontMetrics, aData );
1842}
1843
1844
1846{
1847 if( aPositive )
1848 fmt::println( m_outputFile, "%LPD*%" );
1849 else
1850 fmt::println( m_outputFile, "%LPC*%" );
1851}
1852
1853
1854bool APER_MACRO_FREEPOLY::IsSamePoly( const std::vector<VECTOR2I>& aPolygon ) const
1855{
1856 return polyCompare( m_Corners, aPolygon );
1857}
1858
1859
1860void APER_MACRO_FREEPOLY::Format( FILE * aOutput, double aIu2GbrMacroUnit )
1861{
1862 // Write aperture header
1863 fmt::println( aOutput, "%AM{}{}*", AM_FREEPOLY_BASENAME, m_Id );
1864 fmt::print( aOutput, "4,1,{},", (int) m_Corners.size() );
1865
1866 // Insert a newline after curr_line_count_max coordinates.
1867 int curr_line_corner_count = 0;
1868 const int curr_line_count_max = 20; // <= 0 to disable newlines
1869
1870 for( size_t ii = 0; ii <= m_Corners.size(); ii++ )
1871 {
1872 int jj = ii;
1873
1874 if( ii >= m_Corners.size() )
1875 jj = 0;
1876
1877 // Note: parameter values are always mm or inches
1878 fmt::print( aOutput, "{:#f},{:#f},",
1879 m_Corners[jj].x * aIu2GbrMacroUnit, -m_Corners[jj].y * aIu2GbrMacroUnit );
1880
1881 if( curr_line_count_max >= 0 && ++curr_line_corner_count >= curr_line_count_max )
1882 {
1883 fmt::println( aOutput, "" );
1884 curr_line_corner_count = 0;
1885 }
1886 }
1887
1888 // output rotation parameter
1889 fmt::println( aOutput, "$1*%" );
1890}
1891
1892
1893void APER_MACRO_FREEPOLY_LIST::Format( FILE * aOutput, double aIu2GbrMacroUnit )
1894{
1895 for( int idx = 0; idx < AmCount(); idx++ )
1896 m_AMList[idx].Format( aOutput, aIu2GbrMacroUnit );
1897}
1898
1899
1900void APER_MACRO_FREEPOLY_LIST::Append( const std::vector<VECTOR2I>& aPolygon )
1901{
1902 m_AMList.emplace_back( aPolygon, AmCount() );
1903}
1904
1905
1906int APER_MACRO_FREEPOLY_LIST::FindAm( const std::vector<VECTOR2I>& aPolygon ) const
1907{
1908 for( int idx = 0; idx < AmCount(); idx++ )
1909 {
1910 if( m_AMList[idx].IsSamePoly( aPolygon ) )
1911 return idx;
1912 }
1913
1914 return -1;
1915}
#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
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
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
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:146
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:678
virtual void ThickPoly(const SHAPE_POLY_SET &aPoly, int aWidth, void *aData)
Definition plotter.cpp:604
bool m_plotMirror
Definition plotter.h:655
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:652
VECTOR2I m_plotOffset
Definition plotter.h:654
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:676
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:667
wxString m_creator
Definition plotter.h:670
int m_currentPenWidth
Definition plotter.h:666
double m_plotScale
Plot scale - chosen by the user (even implicitly with 'fit in a4')
Definition plotter.h:644
FILE * m_outputFile
Output file.
Definition plotter.h:661
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:680
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:650
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:118
bool IsClockwise() const
Definition shape_arc.h:321
const VECTOR2I & GetP1() const
Definition shape_arc.h:117
const VECTOR2I & GetP0() const
Definition shape_arc.h:116
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
const SHAPE_LINE_CHAIN Outline() const
void SetRadius(int aRadius)
Definition shape_rect.h:206
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