KiCad PCB EDA Suite
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 (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation, either version 3 of the License, or (at your
10  * option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
26 #include <eda_base_frame.h>
27 #include <fill_type.h>
28 #include <kicad_string.h>
30 #include <macros.h>
31 #include <math/util.h> // for KiROUND
32 #include <render_settings.h>
33 #include <trigo.h>
34 
35 #include <build_version.h>
36 
37 #include "plotter_gerber.h"
39 
40 #include <gbr_metadata.h>
41 
42 
43 // if GBR_USE_MACROS is defined, pads having a shape that is not a Gerber primitive
44 // will use a macro when possible
45 // Old code will be removed only after many tests
46 //
47 // Note also: setting m_gerberDisableApertMacros to true disable all aperture macros
48 // in Gerber files
49 //
50 #define GBR_USE_MACROS_FOR_CHAMFERED_ROUND_RECT
51 #define GBR_USE_MACROS_FOR_CHAMFERED_RECT
52 #define GBR_USE_MACROS_FOR_ROUNDRECT
53 #define GBR_USE_MACROS_FOR_TRAPEZOID
54 #define GBR_USE_MACROS_FOR_ROTATED_OVAL
55 #define GBR_USE_MACROS_FOR_ROTATED_RECT
56 #define GBR_USE_MACROS_FOR_CUSTOM_PAD
57 
58 // max count of corners to create a aperture macro for a custom shape.
59 // provided just in case a aperture macro type free polygon creates issues
60 // when the number of corners is too high.
61 // (1 corner = up to 24 chars)
62 // Gerber doc say max corners 5000. We use a slightly smaller value.
63 // if a custom shape needs more than GBR_MACRO_FOR_CUSTOM_PAD_MAX_CORNER_COUNT, it
64 // will be plot using a region.
65 #define GBR_MACRO_FOR_CUSTOM_PAD_MAX_CORNER_COUNT 4990
66 #define AM_FREEPOLY_BASENAME "FreePoly"
67 
68 // A helper function to compare 2 polygons: polygons are similar if they havve the same
69 // number of vertices and each vertex coordinate are similar, i.e. if the difference
70 // between coordinates is small ( <= margin to accept rounding issues coming from polygon
71 // geometric transforms like rotation
72 static bool polyCompare( const std::vector<wxPoint>& aPolygon,
73  const std::vector<wxPoint>& aTestPolygon )
74 {
75  // fast test: polygon sizes must be the same:
76  if( aTestPolygon.size() != aPolygon.size() )
77  return false;
78 
79  const int margin = 2;
80 
81  for( size_t jj = 0; jj < aPolygon.size(); jj++ )
82  {
83  if( std::abs( aPolygon[jj].x - aTestPolygon[jj].x ) > margin ||
84  std::abs( aPolygon[jj].y - aTestPolygon[jj].y ) > margin )
85  return false;
86  }
87 
88  return true;
89 }
90 
91 
93 {
94  workFile = nullptr;
95  finalFile = nullptr;
98 
99  // number of digits after the point (number of digits of the mantissa
100  // Be careful: the Gerber coordinates are stored in an integer
101  // so 6 digits (inches) or 5 digits (mm) is a good value
102  // To avoid overflow, 7 digits (inches) or 6 digits is a max.
103  // with lower values than 6 digits (inches) or 5 digits (mm),
104  // Creating self-intersecting polygons from non-intersecting polygons
105  // happen easily.
106  m_gerberUnitInch = false;
107  m_gerberUnitFmt = 6;
108  m_useX2format = true;
109  m_useNetAttributes = true;
111 
112  m_hasApertureRoundRect = false; // true is at least one round rect aperture is in use
113  m_hasApertureRotOval = false; // true is at least one oval rotated aperture is in use
114  m_hasApertureRotRect = false; // true is at least one rect. rotated aperture is in use
115  m_hasApertureOutline4P = false; // true is at least one rotated rect or trapezoid pad
116  // aperture is in use
117  m_hasApertureChamferedRect = false; // true is at least one chamfered rect
118  // (no rounded corner) is in use
119 }
120 
121 
122 void GERBER_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil,
123  double aScale, bool aMirror )
124 {
125  wxASSERT( aMirror == false );
126  m_plotMirror = false;
127  m_plotOffset = aOffset;
128  wxASSERT( aScale == 1 ); // aScale parameter is not used in Gerber
129  m_plotScale = 1; // Plot scale is *always* 1.0
130 
131  m_IUsPerDecimil = aIusPerDecimil;
132  // gives now a default value to iuPerDeviceUnit (because the units of the caller is now known)
133  // which could be modified later by calling SetGerberCoordinatesFormat()
134  m_iuPerDeviceUnit = pow( 10.0, m_gerberUnitFmt ) / ( m_IUsPerDecimil * 10000.0 );
135 
136  // We don't handle the filmbox, and it's more useful to keep the
137  // origin at the origin
138  m_paperSize.x = 0;
139  m_paperSize.y = 0;
140 }
141 
142 
143 void GERBER_PLOTTER::SetGerberCoordinatesFormat( int aResolution, bool aUseInches )
144 {
145  m_gerberUnitInch = aUseInches;
146  m_gerberUnitFmt = aResolution;
147 
148  m_iuPerDeviceUnit = pow( 10.0, m_gerberUnitFmt ) / ( m_IUsPerDecimil * 10000.0 );
149 
150  if( ! m_gerberUnitInch )
151  m_iuPerDeviceUnit *= 25.4; // gerber output in mm
152 }
153 
154 
155 void GERBER_PLOTTER::emitDcode( const DPOINT& pt, int dcode )
156 {
157 
158  fprintf( m_outputFile, "X%dY%dD%02d*\n", KiROUND( pt.x ), KiROUND( pt.y ), dcode );
159 }
160 
162 {
163  // Remove all attributes from object attributes dictionary (TO. and TA commands)
164  if( m_useX2format )
165  fputs( "%TD*%\n", m_outputFile );
166  else
167  fputs( "G04 #@! TD*\n", m_outputFile );
168 
170 }
171 
172 
174 {
175  // disable a Gerber net attribute (exists only in X2 with net attributes mode).
176  if( m_objectAttributesDictionary.empty() ) // No net attribute or not X2 mode
177  return;
178 
179  // Remove all net attributes from object attributes dictionary
180  if( m_useX2format )
181  fputs( "%TD*%\n", m_outputFile );
182  else
183  fputs( "G04 #@! TD*\n", m_outputFile );
184 
186 }
187 
188 
189 void GERBER_PLOTTER::StartBlock( void* aData )
190 {
191  // Currently, it is the same as EndBlock(): clear all aperture net attributes
192  EndBlock( aData );
193 }
194 
195 
196 void GERBER_PLOTTER::EndBlock( void* aData )
197 {
198  // Remove all net attributes from object attributes dictionary
200 }
201 
202 
204 {
205  // print a Gerber net attribute record.
206  // it is added to the object attributes dictionary
207  // On file, only modified or new attributes are printed.
208  if( aData == nullptr )
209  return;
210 
211  if( !m_useNetAttributes )
212  return;
213 
214  bool useX1StructuredComment = !m_useX2format;
215 
216  bool clearDict;
217  std::string short_attribute_string;
218 
219  if( !FormatNetAttribute( short_attribute_string, m_objectAttributesDictionary,
220  aData, clearDict, useX1StructuredComment ) )
221  return;
222 
223  if( clearDict )
225 
226  if( !short_attribute_string.empty() )
227  fputs( short_attribute_string.c_str(), m_outputFile );
228 
229  if( m_useX2format && !aData->m_ExtraData.IsEmpty() )
230  {
231  std::string extra_data = TO_UTF8( aData->m_ExtraData );
232  fputs( extra_data.c_str(), m_outputFile );
233  }
234 }
235 
236 
238 {
239  m_hasApertureRoundRect = false; // true is at least one round rect aperture is in use
240  m_hasApertureRotOval = false; // true is at least one oval rotated aperture is in use
241  m_hasApertureRotRect = false; // true is at least one rect. rotated aperture is in use
242  m_hasApertureOutline4P = false; // true is at least one rotated rect/trapezoid aperture is in use
243  m_hasApertureChamferedRect = false; // true is at least one chamfered rect is in use
245 
246  wxASSERT( m_outputFile );
247 
248  finalFile = m_outputFile; // the actual gerber file will be created later
249 
250  // Create a temp file in system temp to avoid potential network share buffer issues for the final read and save
251  m_workFilename = wxFileName::CreateTempFileName( "" );
252  workFile = wxFopen( m_workFilename, wxT( "wt" ));
254  wxASSERT( m_outputFile );
255 
256  if( m_outputFile == nullptr )
257  return false;
258 
259  for( unsigned ii = 0; ii < m_headerExtraLines.GetCount(); ii++ )
260  {
261  if( ! m_headerExtraLines[ii].IsEmpty() )
262  fprintf( m_outputFile, "%s\n", TO_UTF8( m_headerExtraLines[ii] ) );
263  }
264 
265  // Set coordinate format to 3.6 or 4.5 absolute, leading zero omitted
266  // the number of digits for the integer part of coordinates is needed
267  // in gerber format, but is not very important when omitting leading zeros
268  // It is fixed here to 3 (inch) or 4 (mm), but is not actually used
269  int leadingDigitCount = m_gerberUnitInch ? 3 : 4;
270 
271  fprintf( m_outputFile, "%%FSLAX%d%dY%d%d*%%\n",
272  leadingDigitCount, m_gerberUnitFmt,
273  leadingDigitCount, m_gerberUnitFmt );
274  fprintf( m_outputFile,
275  "G04 Gerber Fmt %d.%d, Leading zero omitted, Abs format (unit %s)*\n",
276  leadingDigitCount, m_gerberUnitFmt,
277  m_gerberUnitInch ? "inch" : "mm" );
278 
279  wxString Title = m_creator + wxT( " " ) + GetBuildVersion();
280  // In gerber files, ASCII7 chars only are allowed.
281  // So use a ISO date format (using a space as separator between date and time),
282  // not a localized date format
283  wxDateTime date = wxDateTime::Now();
284  fprintf( m_outputFile, "G04 Created by KiCad (%s) date %s*\n",
285  TO_UTF8( Title ), TO_UTF8( date.FormatISOCombined( ' ') ) );
286 
287  /* Mass parameter: unit = INCHES/MM */
288  if( m_gerberUnitInch )
289  fputs( "%MOIN*%\n", m_outputFile );
290  else
291  fputs( "%MOMM*%\n", m_outputFile );
292 
293  // Be sure the usual dark polarity is selected:
294  fputs( "%LPD*%\n", m_outputFile );
295 
296  // Set initial interpolation mode: always G01 (linear):
297  fputs( "G01*\n", m_outputFile );
298 
299  // Add aperture list start point
300  fputs( "G04 APERTURE LIST*\n", m_outputFile );
301 
302  // Give a minimal value to the default pen size, used to plot items in sketch mode
303  if( m_renderSettings )
304  {
305  const int pen_min = 0.1 * m_IUsPerDecimil * 10000 / 25.4; // for min width = 0.1 mm
307  pen_min ) );
308  }
309 
310  return true;
311 }
312 
313 
315 {
316  char line[1024];
317  wxString msg;
318 
319  wxASSERT( m_outputFile );
320 
321  /* Outfile is actually a temporary file i.e. workFile */
322  fputs( "M02*\n", m_outputFile );
323  fflush( m_outputFile );
324 
325  fclose( workFile );
326  workFile = wxFopen( m_workFilename, wxT( "rt" ));
327  wxASSERT( workFile );
329 
330  // Placement of apertures in RS274X
331  while( fgets( line, 1024, workFile ) )
332  {
333  fputs( line, m_outputFile );
334 
335  char* substr = strtok( line, "\n\r" );
336 
337  if( substr && strcmp( substr, "G04 APERTURE LIST*" ) == 0 )
338  {
339  // Add aperture list macro:
343  {
344  fputs( "G04 Aperture macros list*\n", m_outputFile );
345 
348 
351 
354 
357 
359  {
364  }
365 
367  {
368  // apertude sizes are in inch or mm, regardless the
369  // coordinates format
370  double fscale = 0.0001 * m_plotScale / m_IUsPerDecimil; // inches
371 
372  if(! m_gerberUnitInch )
373  fscale *= 25.4; // size in mm
374 
376  }
377 
378  fputs( "G04 Aperture macros list end*\n", m_outputFile );
379  }
380 
382  fputs( "G04 APERTURE END LIST*\n", m_outputFile );
383  }
384  }
385 
386  fclose( workFile );
387  fclose( finalFile );
388  ::wxRemoveFile( m_workFilename );
389  m_outputFile = nullptr;
390 
391  return true;
392 }
393 
394 
395 void GERBER_PLOTTER::SetCurrentLineWidth( int aWidth, void* aData )
396 {
397  if( aWidth == DO_NOT_SET_LINE_WIDTH )
398  return;
399  else if( aWidth == USE_DEFAULT_LINE_WIDTH )
401 
402  wxASSERT_MSG( aWidth >= 0, "Plotter called to set negative pen width" );
403 
404  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
405  int aperture_attribute = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
406 
407  selectAperture( wxSize( aWidth, aWidth ), 0, 0.0, APERTURE::AT_PLOTTING, aperture_attribute );
408  m_currentPenWidth = aWidth;
409 }
410 
411 
412 int GERBER_PLOTTER::GetOrCreateAperture( const wxSize& aSize, int aRadius, double aRotDegree,
413  APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
414 {
415  int last_D_code = 9;
416 
417  // Search an existing aperture
418  for( int idx = 0; idx < (int)m_apertures.size(); ++idx )
419  {
420  APERTURE* tool = &m_apertures[idx];
421  last_D_code = tool->m_DCode;
422 
423  if( (tool->m_Type == aType) && (tool->m_Size == aSize) &&
424  (tool->m_Radius == aRadius) && (tool->m_Rotation == aRotDegree) &&
425  (tool->m_ApertureAttribute == aApertureAttribute) )
426  return idx;
427  }
428 
429  // Allocate a new aperture
430  APERTURE new_tool;
431  new_tool.m_Size = aSize;
432  new_tool.m_Type = aType;
433  new_tool.m_Radius = aRadius;
434  new_tool.m_Rotation = aRotDegree;
435  new_tool.m_DCode = last_D_code + 1;
436  new_tool.m_ApertureAttribute = aApertureAttribute;
437 
438  m_apertures.push_back( new_tool );
439 
440  return m_apertures.size() - 1;
441 }
442 
443 
444 int GERBER_PLOTTER::GetOrCreateAperture( const std::vector<wxPoint>& aCorners, double aRotDegree,
445  APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
446 {
447  int last_D_code = 9;
448 
449  // For APERTURE::AM_FREE_POLYGON aperture macros, we need to create the macro
450  // on the fly, because due to the fact the vertice count is not a constant we
451  // cannot create a static definition.
452  if( APERTURE::AM_FREE_POLYGON == aType )
453  {
454  int idx = m_am_freepoly_list.FindAm( aCorners );
455 
456  if( idx < 0 )
457  m_am_freepoly_list.Append( aCorners );
458  }
459 
460  // Search an existing aperture
461  for( int idx = 0; idx < (int)m_apertures.size(); ++idx )
462  {
463  APERTURE* tool = &m_apertures[idx];
464 
465  last_D_code = tool->m_DCode;
466 
467  if( (tool->m_Type == aType) &&
468  (tool->m_Corners.size() == aCorners.size() ) &&
469  (tool->m_Rotation == aRotDegree) &&
470  (tool->m_ApertureAttribute == aApertureAttribute) )
471  {
472  // A candidate is found. the corner lists must be similar
473  bool is_same = polyCompare( tool->m_Corners, aCorners );
474 
475  if( is_same )
476  return idx;
477  }
478  }
479 
480  // Allocate a new aperture
481  APERTURE new_tool;
482 
483  new_tool.m_Corners = aCorners;
484  new_tool.m_Size = wxSize( 0, 0 ); // Not used
485  new_tool.m_Type = aType;
486  new_tool.m_Radius = 0; // Not used
487  new_tool.m_Rotation = aRotDegree;
488  new_tool.m_DCode = last_D_code + 1;
489  new_tool.m_ApertureAttribute = aApertureAttribute;
490 
491  m_apertures.push_back( new_tool );
492 
493  return m_apertures.size() - 1;
494 }
495 
496 
497 void GERBER_PLOTTER::selectAperture( const wxSize& aSize, int aRadius, double aRotDegree,
499  int aApertureAttribute )
500 {
501  bool change = ( m_currentApertureIdx < 0 ) ||
502  ( m_apertures[m_currentApertureIdx].m_Type != aType ) ||
503  ( m_apertures[m_currentApertureIdx].m_Size != aSize ) ||
504  ( m_apertures[m_currentApertureIdx].m_Radius != aRadius ) ||
505  ( m_apertures[m_currentApertureIdx].m_Rotation != aRotDegree );
506 
507  if( !change )
508  change = m_apertures[m_currentApertureIdx].m_ApertureAttribute != aApertureAttribute;
509 
510  if( change )
511  {
512  // Pick an existing aperture or create a new one
513  m_currentApertureIdx = GetOrCreateAperture( aSize, aRadius, aRotDegree,
514  aType, aApertureAttribute );
515  fprintf( m_outputFile, "D%d*\n", m_apertures[m_currentApertureIdx].m_DCode );
516  }
517 }
518 
519 
520 void GERBER_PLOTTER::selectAperture( const std::vector<wxPoint>& aCorners, double aRotDegree,
521  APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
522 {
523  bool change = ( m_currentApertureIdx < 0 ) ||
524  ( m_apertures[m_currentApertureIdx].m_Type != aType ) ||
525  ( m_apertures[m_currentApertureIdx].m_Corners.size() != aCorners.size() ) ||
526  ( m_apertures[m_currentApertureIdx].m_Rotation != aRotDegree );
527 
528  if( !change ) // Compare corner lists
529  {
530  for( size_t ii = 0; ii < aCorners.size(); ii++ )
531  {
532  if( aCorners[ii] != m_apertures[m_currentApertureIdx].m_Corners[ii] )
533  {
534  change = true;
535  break;
536  }
537  }
538  }
539 
540  if( !change )
541  change = m_apertures[m_currentApertureIdx].m_ApertureAttribute != aApertureAttribute;
542 
543  if( change )
544  {
545  // Pick an existing aperture or create a new one
546  m_currentApertureIdx = GetOrCreateAperture( aCorners, aRotDegree,
547  aType, aApertureAttribute );
548  fprintf( m_outputFile, "D%d*\n", m_apertures[m_currentApertureIdx].m_DCode );
549  }
550 }
551 
552 
553 void GERBER_PLOTTER::selectAperture( int aDiameter, double aPolygonRotation,
554  APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
555 {
556  // Pick an existing aperture or create a new one, matching the
557  // aDiameter, aPolygonRotation, type and attributes for type =
558  // AT_REGULAR_POLY3 to AT_REGULAR_POLY12
559 
560  wxASSERT( aType>= APERTURE::APERTURE_TYPE::AT_REGULAR_POLY3 &&
561  aType <= APERTURE::APERTURE_TYPE::AT_REGULAR_POLY12 );
562 
563  wxSize size( aDiameter, (int)( aPolygonRotation * 1000.0 ) );
564  selectAperture( wxSize( 0, 0), aDiameter/2, aPolygonRotation, aType, aApertureAttribute );
565 }
566 
568 {
569  wxASSERT( m_outputFile );
570  char cbuf[1024];
571  std::string buffer;
572 
573  bool useX1StructuredComment = false;
574 
575  if( !m_useX2format )
576  useX1StructuredComment = true;
577 
578  // Init
579  for( APERTURE& tool : m_apertures )
580  {
581  // apertude sizes are in inch or mm, regardless the
582  // coordinates format
583  double fscale = 0.0001 * m_plotScale / m_IUsPerDecimil; // inches
584 
585  if(! m_gerberUnitInch )
586  fscale *= 25.4; // size in mm
587 
588  int attribute = tool.m_ApertureAttribute;
589 
590  if( attribute != m_apertureAttribute )
591  {
594  useX1StructuredComment ).c_str(), m_outputFile );
595  }
596 
597  sprintf( cbuf, "%%ADD%d", tool.m_DCode );
598  buffer = cbuf;
599 
600  /* Please note: the Gerber specs for mass parameters say that
601  exponential syntax is *not* allowed and the decimal point should
602  also be always inserted. So the %g format is ruled out, but %f is fine
603  (the # modifier forces the decimal point). Sadly the %f formatter
604  can't remove trailing zeros but thats not a problem, since nothing
605  forbid it (the file is only slightly longer) */
606 
607  switch( tool.m_Type )
608  {
609  case APERTURE::AT_CIRCLE:
610  sprintf( cbuf, "C,%#f*%%\n", tool.GetDiameter() * fscale );
611  break;
612 
613  case APERTURE::AT_RECT:
614  sprintf( cbuf, "R,%#fX%#f*%%\n", tool.m_Size.x * fscale,
615  tool.m_Size.y * fscale );
616  break;
617 
619  sprintf( cbuf, "C,%#f*%%\n", tool.m_Size.x * fscale );
620  break;
621 
622  case APERTURE::AT_OVAL:
623  sprintf( cbuf, "O,%#fX%#f*%%\n", tool.m_Size.x * fscale,
624  tool.m_Size.y * fscale );
625  break;
626 
638  sprintf( cbuf, "P,%#fX%dX%#f*%%\n", tool.GetDiameter() * fscale,
639  tool.GetRegPolyVerticeCount(), tool.GetRotation() );
640  break;
641 
642  case APERTURE::AM_ROUND_RECT: // Aperture macro for round rect pads
643  {
644  // The aperture macro needs coordinates of the centers of the 4 corners
645  std::vector<VECTOR2I> corners;
646  wxSize half_size( tool.m_Size.x/2-tool.m_Radius, tool.m_Size.y/2-tool.m_Radius );
647 
648  corners.emplace_back( -half_size.x, -half_size.y );
649  corners.emplace_back( half_size.x, -half_size.y );
650  corners.emplace_back( half_size.x, half_size.y );
651  corners.emplace_back( -half_size.x, half_size.y );
652 
653  // Rotate the corner coordinates:
654  for( int ii = 0; ii < 4; ii++ )
655  RotatePoint( corners[ii], -tool.m_Rotation*10.0 );
656 
657  sprintf( cbuf, "%s,%#fX", APER_MACRO_ROUNDRECT_NAME,
658  tool.m_Radius * fscale );
659  buffer += cbuf;
660 
661  // Add each corner
662  for( int ii = 0; ii < 4; ii++ )
663  {
664  sprintf( cbuf, "%#fX%#fX",
665  corners[ii].x * fscale, corners[ii].y * fscale );
666  buffer += cbuf;
667  }
668 
669  sprintf( cbuf, "0*%%\n" );
670  }
671  break;
672 
673  case APERTURE::AM_ROT_RECT: // Aperture macro for rotated rect pads
674  sprintf( cbuf, "%s,%#fX%#fX%#f*%%\n", APER_MACRO_ROT_RECT_NAME,
675  tool.m_Size.x * fscale, tool.m_Size.y * fscale,
676  tool.m_Rotation );
677  break;
678 
679  case APERTURE::APER_MACRO_OUTLINE4P: // Aperture macro for trapezoid pads
680  case APERTURE::APER_MACRO_OUTLINE5P: // Aperture macro for chamfered rect pads
681  case APERTURE::APER_MACRO_OUTLINE6P: // Aperture macro for chamfered rect pads
682  case APERTURE::APER_MACRO_OUTLINE7P: // Aperture macro for chamfered rect pads
683  case APERTURE::APER_MACRO_OUTLINE8P: // Aperture macro for chamfered rect pads
684  switch( tool.m_Type )
685  {
687  sprintf( cbuf, "%s,", APER_MACRO_OUTLINE4P_NAME ); break;
689  sprintf( cbuf, "%s,", APER_MACRO_OUTLINE5P_NAME ); break;
691  sprintf( cbuf, "%s,", APER_MACRO_OUTLINE6P_NAME ); break;
693  sprintf( cbuf, "%s,", APER_MACRO_OUTLINE7P_NAME ); break;
695  sprintf( cbuf, "%s,", APER_MACRO_OUTLINE8P_NAME ); break;
696  default:
697  break;
698  }
699 
700  buffer += cbuf;
701 
702  // Output all corners (should be 4 to 8 corners)
703  // Remember: the Y coordinate must be negated, due to the fact in Pcbnew
704  // the Y axis is from top to bottom
705  for( size_t ii = 0; ii < tool.m_Corners.size(); ii++ )
706  {
707  sprintf( cbuf, "%#fX%#fX",
708  tool.m_Corners[ii].x * fscale, -tool.m_Corners[ii].y * fscale );
709  buffer += cbuf;
710  }
711 
712  // close outline and output rotation
713  sprintf( cbuf, "%#f*%%\n", tool.m_Rotation );
714  break;
715 
716  case APERTURE::AM_ROTATED_OVAL: // Aperture macro for rotated oval pads
717  // (not rotated is a primitive)
718  // m_Size.x = full lenght; m_Size.y = width, and the macro aperure expects
719  // the position of ends
720  {
721  // the seg_len is the distance between the 2 circle centers
722  int seg_len = tool.m_Size.x - tool.m_Size.y;
723  // Center of the circle on the segment start point:
724  VECTOR2I start( seg_len/2, 0 );
725  // Center of the circle on the segment end point:
726  VECTOR2I end( - seg_len/2, 0 );
727 
728  RotatePoint( start, tool.m_Rotation*10.0 );
729  RotatePoint( end, tool.m_Rotation*10.0 );
730 
731  sprintf( cbuf, "%s,%#fX%#fX%#fX%#fX%#fX0*%%\n", APER_MACRO_SHAPE_OVAL_NAME,
732  tool.m_Size.y * fscale, // width
733  start.x * fscale, -start.y * fscale, // X,Y corner start pos
734  end.x * fscale, -end.y * fscale ); // X,Y cornerend pos
735  }
736  break;
737 
739  {
740  // Find the aperture macro name in the list of aperture macro
741  // created on the fly for this polygon:
742  int idx = m_am_freepoly_list.FindAm( tool.m_Corners );
743 
744  // Write DCODE id ( "%ADDxx" is already in buffer) and rotation
745  // the full line is something like :%ADD12FreePoly1,45.000000*%
746  sprintf( cbuf, "%s%d,%#f*%%\n", AM_FREEPOLY_BASENAME, idx, tool.m_Rotation );
747  }
748  break;
749  }
750 
751  buffer += cbuf;
752  fputs( buffer.c_str(), m_outputFile );
753 
754  m_apertureAttribute = attribute;
755 
756  // Currently reset the aperture attribute. Perhaps a better optimization
757  // is to store the last attribute
758  if( attribute )
759  {
760  if( m_useX2format )
761  fputs( "%TD*%\n", m_outputFile );
762  else
763  fputs( "G04 #@! TD*\n", m_outputFile );
764 
766  }
767 
768  }
769 }
770 
771 
772 void GERBER_PLOTTER::PenTo( const wxPoint& aPos, char plume )
773 {
774  wxASSERT( m_outputFile );
775  DPOINT pos_dev = userToDeviceCoordinates( aPos );
776 
777  switch( plume )
778  {
779  case 'Z':
780  break;
781 
782  case 'U':
783  emitDcode( pos_dev, 2 );
784  break;
785 
786  case 'D':
787  emitDcode( pos_dev, 1 );
788  }
789 
790  m_penState = plume;
791 }
792 
793 
794 void GERBER_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_TYPE fill, int width )
795 {
796  std::vector< wxPoint > cornerList;
797 
798  // Build corners list
799  cornerList.push_back( p1 );
800  wxPoint corner(p1.x, p2.y);
801  cornerList.push_back( corner );
802  cornerList.push_back( p2 );
803  corner.x = p2.x;
804  corner.y = p1.y;
805  cornerList.push_back( corner );
806  cornerList.push_back( p1 );
807 
808  PlotPoly( cornerList, fill, width );
809 }
810 
811 
812 void GERBER_PLOTTER::Circle( const wxPoint& aCenter, int aDiameter, FILL_TYPE aFill, int aWidth )
813 {
814  Arc( aCenter, 0, 3600, aDiameter / 2, aFill, aWidth );
815 }
816 
817 
818 void GERBER_PLOTTER::Arc( const wxPoint& aCenter, double aStAngle, double aEndAngle,
819  int aRadius, FILL_TYPE aFill, int aWidth )
820 {
821  SetCurrentLineWidth( aWidth );
822 
823  // aFill is not used here.
824  plotArc( aCenter, aStAngle, aEndAngle, aRadius, false );
825 }
826 
827 
828 void GERBER_PLOTTER::plotArc( const wxPoint& aCenter, double aStAngle, double aEndAngle,
829  int aRadius, bool aPlotInRegion )
830 {
831  wxPoint start, end;
832  start.x = aCenter.x + KiROUND( cosdecideg( aRadius, aStAngle ) );
833  start.y = aCenter.y - KiROUND( sindecideg( aRadius, aStAngle ) );
834 
835  if( !aPlotInRegion )
836  MoveTo( start );
837  else
838  LineTo( start );
839 
840  end.x = aCenter.x + KiROUND( cosdecideg( aRadius, aEndAngle ) );
841  end.y = aCenter.y - KiROUND( sindecideg( aRadius, aEndAngle ) );
842  DPOINT devEnd = userToDeviceCoordinates( end );
843  DPOINT devCenter = userToDeviceCoordinates( aCenter ) - userToDeviceCoordinates( start );
844 
845  fprintf( m_outputFile, "G75*\n" ); // Multiquadrant (360 degrees) mode
846 
847  if( aStAngle < aEndAngle )
848  fprintf( m_outputFile, "G03*\n" ); // Active circular interpolation, CCW
849  else
850  fprintf( m_outputFile, "G02*\n" ); // Active circular interpolation, CW
851 
852  fprintf( m_outputFile, "X%dY%dI%dJ%dD01*\n",
853  KiROUND( devEnd.x ), KiROUND( devEnd.y ),
854  KiROUND( devCenter.x ), KiROUND( devCenter.y ) );
855 
856  fprintf( m_outputFile, "G01*\n" ); // Back to linear interpol (perhaps useless here).
857 }
858 
859 
860 void GERBER_PLOTTER::PlotGerberRegion( const std::vector< wxPoint >& aCornerList,
861  void * aData )
862 {
863  if( aCornerList.size() <= 2 )
864  return;
865 
866  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
867 
868  bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
869 
870  if( gbr_metadata )
871  {
872  std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
873 
874  if( !attrib.empty() )
875  {
876  fputs( attrib.c_str(), m_outputFile );
877  clearTA_AperFunction = true;
878  }
879  }
880 
881  PlotPoly( aCornerList, FILL_TYPE::FILLED_SHAPE, 0, gbr_metadata );
882 
883  // Clear the TA attribute, to avoid the next item to inherit it:
884  if( clearTA_AperFunction )
885  {
886  if( m_useX2format )
887  {
888  fputs( "%TD.AperFunction*%\n", m_outputFile );
889  }
890  else
891  {
892  fputs( "G04 #@! TD.AperFunction*\n", m_outputFile );
893  }
894  }
895 }
896 
897 void GERBER_PLOTTER::PlotPoly( const std::vector< wxPoint >& aCornerList,
898  FILL_TYPE aFill, int aWidth, void * aData )
899 {
900  if( aCornerList.size() <= 1 )
901  return;
902 
903  // Gerber format does not know filled polygons with thick outline
904  // Therefore, to plot a filled polygon with outline having a thickness,
905  // one should plot outline as thick segments
906  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
907 
908  if( gbr_metadata )
909  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
910 
911  if( aFill != FILL_TYPE::NO_FILL )
912  {
913  fputs( "G36*\n", m_outputFile );
914 
915  MoveTo( aCornerList[0] );
916  fputs( "G01*\n", m_outputFile ); // Set linear interpolation.
917 
918  for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
919  LineTo( aCornerList[ii] );
920 
921  // If the polygon is not closed, close it:
922  if( aCornerList[0] != aCornerList[aCornerList.size()-1] )
923  FinishTo( aCornerList[0] );
924 
925  fputs( "G37*\n", m_outputFile );
926  }
927 
928  if( aWidth > 0 ) // Draw the polyline/polygon outline
929  {
930  SetCurrentLineWidth( aWidth, gbr_metadata );
931 
932  MoveTo( aCornerList[0] );
933 
934  for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
935  LineTo( aCornerList[ii] );
936 
937  // Ensure the thick outline is closed for filled polygons
938  // (if not filled, could be only a polyline)
939  if( aFill != FILL_TYPE::NO_FILL &&( aCornerList[aCornerList.size() - 1] != aCornerList[0] ) )
940  LineTo( aCornerList[0] );
941 
942  PenFinish();
943  }
944 }
945 
946 
947 void GERBER_PLOTTER::ThickSegment( const wxPoint& start, const wxPoint& end, int width,
948  OUTLINE_MODE tracemode, void* aData )
949 {
950  if( tracemode == FILLED )
951  {
952  GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
953  SetCurrentLineWidth( width, gbr_metadata );
954 
955  if( gbr_metadata )
956  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
957 
958  MoveTo( start );
959  FinishTo( end );
960  }
961  else
962  {
964  segmentAsOval( start, end, width, tracemode );
965  }
966 }
967 
968 void GERBER_PLOTTER::ThickArc( const wxPoint& centre, double StAngle, double EndAngle,
969  int radius, int width, OUTLINE_MODE tracemode, void* aData )
970 {
971  GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
972  SetCurrentLineWidth( width, gbr_metadata );
973 
974  if( gbr_metadata )
975  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
976 
977  if( tracemode == FILLED )
978  Arc( centre, StAngle, EndAngle, radius, FILL_TYPE::NO_FILL, DO_NOT_SET_LINE_WIDTH );
979  else
980  {
982  Arc( centre, StAngle, EndAngle,
983  radius - ( width - m_currentPenWidth ) / 2, FILL_TYPE::NO_FILL,
985  Arc( centre, StAngle, EndAngle, radius + ( width - m_currentPenWidth ) / 2, FILL_TYPE::NO_FILL,
987  }
988 }
989 
990 
991 void GERBER_PLOTTER::ThickRect( const wxPoint& p1, const wxPoint& p2, int width,
992  OUTLINE_MODE tracemode, void* aData )
993 {
994  GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
995  SetCurrentLineWidth( width, gbr_metadata );
996 
997  if( gbr_metadata )
998  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
999 
1000  if( tracemode == FILLED )
1002  else
1003  {
1005  wxPoint offsetp1( p1.x - (width - m_currentPenWidth) / 2,
1006  p1.y - (width - m_currentPenWidth) / 2 );
1007  wxPoint offsetp2( p2.x + (width - m_currentPenWidth) / 2,
1008  p2.y + (width - m_currentPenWidth) / 2 );
1009  Rect( offsetp1, offsetp2, FILL_TYPE::NO_FILL, -1 );
1010  offsetp1.x += (width - m_currentPenWidth);
1011  offsetp1.y += (width - m_currentPenWidth);
1012  offsetp2.x -= (width - m_currentPenWidth);
1013  offsetp2.y -= (width - m_currentPenWidth);
1014  Rect( offsetp1, offsetp2, FILL_TYPE::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1015  }
1016 }
1017 
1018 
1019 void GERBER_PLOTTER::ThickCircle( const wxPoint& pos, int diametre, int width,
1020  OUTLINE_MODE tracemode, void* aData )
1021 {
1022  GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1023  SetCurrentLineWidth( width, gbr_metadata );
1024 
1025  if( gbr_metadata )
1026  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1027 
1028  if( tracemode == FILLED )
1029  Circle( pos, diametre, FILL_TYPE::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1030  else
1031  {
1033  Circle( pos, diametre - (width - m_currentPenWidth),
1035  Circle( pos, diametre + (width - m_currentPenWidth),
1037  }
1038 }
1039 
1040 
1041 void GERBER_PLOTTER::FilledCircle( const wxPoint& pos, int diametre,
1042  OUTLINE_MODE tracemode, void* aData )
1043 {
1044  // A filled circle is a graphic item, not a pad.
1045  // So it is drawn, not flashed.
1046  GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1047 
1048  if( gbr_metadata )
1049  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1050 
1051  if( tracemode == FILLED )
1052  {
1053  // Draw a circle of diameter = diametre/2 with a line thickness = radius,
1054  // To create a filled circle
1055  SetCurrentLineWidth( diametre/2, gbr_metadata );
1056  Circle( pos, diametre/2, FILL_TYPE::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1057  }
1058  else
1059  {
1061  Circle( pos, diametre, FILL_TYPE::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1062  }
1063 }
1064 
1065 
1066 void GERBER_PLOTTER::FlashPadCircle( const wxPoint& pos, int diametre, OUTLINE_MODE trace_mode, void* aData )
1067 {
1068  wxSize size( diametre, diametre );
1069  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1070 
1071  if( trace_mode == SKETCH )
1072  {
1073  if( gbr_metadata )
1074  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1075 
1077 
1079  }
1080  else
1081  {
1082  DPOINT pos_dev = userToDeviceCoordinates( pos );
1083 
1084  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1085  selectAperture( size, 0, 0.0, APERTURE::AT_CIRCLE, aperture_attrib );
1086 
1087  if( gbr_metadata )
1088  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1089 
1090  emitDcode( pos_dev, 3 );
1091  }
1092 }
1093 
1094 
1095 void GERBER_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double orient,
1096  OUTLINE_MODE trace_mode, void* aData )
1097 {
1098  wxASSERT( m_outputFile );
1099  wxSize size( aSize );
1100  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1101 
1102  // Flash a vertical or horizontal shape (this is a basic aperture).
1103  if( ( orient == 0 || orient == 900 || orient == 1800 || orient == 2700 )
1104  && trace_mode == FILLED )
1105  {
1106  if( orient == 900 || orient == 2700 ) /* orientation turned 90 deg. */
1107  std::swap( size.x, size.y );
1108 
1109  DPOINT pos_dev = userToDeviceCoordinates( pos );
1110  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1111  selectAperture( size, 0, 0.0, APERTURE::AT_OVAL, aperture_attrib );
1112 
1113  if( gbr_metadata )
1114  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1115 
1116  emitDcode( pos_dev, 3 );
1117  }
1118  else // Plot pad as region.
1119  // Only regions and flashed items accept a object attribute TO.P for the pin name
1120  {
1121  if( trace_mode == FILLED )
1122  {
1123  #ifdef GBR_USE_MACROS_FOR_ROTATED_OVAL
1125  #endif
1126  {
1127  m_hasApertureRotOval = true;
1128  // We are using a aperture macro that expect size.y < size.x
1129  // i.e draw a horizontal line for rotation = 0.0
1130  // size.x = length, size.y = width
1131  if( size.x < size.y )
1132  {
1133  std::swap( size.x, size.y );
1134  orient += 900;
1135 
1136  if( orient > 1800 )
1137  orient -= 1800;
1138  }
1139 
1140  DPOINT pos_dev = userToDeviceCoordinates( pos );
1141  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1142  selectAperture( size, 0, orient/10.0, APERTURE::AM_ROTATED_OVAL, aperture_attrib );
1143 
1144  if( gbr_metadata )
1145  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1146 
1147  emitDcode( pos_dev, 3 );
1148  return;
1149  }
1150  // Draw the oval as round rect pad with a radius = 50% min size)
1151  // In gerber file, it will be drawn as a region with arcs, and can be
1152  // detected as pads (similar to a flashed pad)
1153  FlashPadRoundRect( pos, aSize, std::min( aSize.x, aSize.y ) /2,
1154  orient, FILLED, aData );
1155  }
1156  else // Non filled shape: plot outlines:
1157  {
1158  if( size.x > size.y )
1159  {
1160  std::swap( size.x, size.y );
1161 
1162  if( orient < 2700 )
1163  orient += 900;
1164  else
1165  orient -= 2700;
1166  }
1167 
1168  sketchOval( pos, size, orient, -1 );
1169  }
1170  }
1171 }
1172 
1173 
1174 void GERBER_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& aSize,
1175  double orient, OUTLINE_MODE trace_mode, void* aData )
1176 
1177 {
1178  wxASSERT( m_outputFile );
1179  wxSize size( aSize );
1180  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1181 
1182  // Plot as an aperture flash
1183  switch( int( orient ) )
1184  {
1185  case 900:
1186  case 2700: // rotation of 90 degrees or 270 swaps sizes
1187  std::swap( size.x, size.y );
1189 
1190  case 0:
1191  case 1800:
1192  if( trace_mode == SKETCH )
1193  {
1194  if( gbr_metadata )
1195  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1196 
1198  Rect( wxPoint( pos.x - (size.x - GetCurrentLineWidth()) / 2,
1199  pos.y - (size.y - GetCurrentLineWidth()) / 2 ),
1200  wxPoint( pos.x + (size.x - GetCurrentLineWidth()) / 2,
1201  pos.y + (size.y - GetCurrentLineWidth()) / 2 ),
1203  }
1204  else
1205  {
1206  DPOINT pos_dev = userToDeviceCoordinates( pos );
1207  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1208  selectAperture( size, 0, 0.0, APERTURE::AT_RECT, aperture_attrib );
1209 
1210  if( gbr_metadata )
1211  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1212 
1213  emitDcode( pos_dev, 3 );
1214  }
1215  break;
1216 
1217  default:
1218  #ifdef GBR_USE_MACROS_FOR_ROTATED_RECT
1219  if( trace_mode != SKETCH && !m_gerberDisableApertMacros )
1220  {
1221  m_hasApertureRotRect = true;
1222  DPOINT pos_dev = userToDeviceCoordinates( pos );
1223  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1224  selectAperture( size, 0, orient/10.0, APERTURE::AM_ROT_RECT, aperture_attrib );
1225 
1226  if( gbr_metadata )
1227  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1228 
1229  emitDcode( pos_dev, 3 );
1230 
1231  break;
1232  }
1233  #endif
1234  {
1235  // plot pad shape as Gerber region
1236  wxPoint coord[4];
1237  // coord[0] is assumed the lower left
1238  // coord[1] is assumed the upper left
1239  // coord[2] is assumed the upper right
1240  // coord[3] is assumed the lower right
1241 
1242  coord[0].x = -size.x/2; // lower left
1243  coord[0].y = size.y/2;
1244  coord[1].x = -size.x/2; // upper left
1245  coord[1].y = -size.y/2;
1246  coord[2].x = size.x/2; // upper right
1247  coord[2].y = -size.y/2;
1248  coord[3].x = size.x/2; // lower right
1249  coord[3].y = size.y/2;
1250 
1251  FlashPadTrapez( pos, coord, orient, trace_mode, aData );
1252  }
1253  break;
1254  }
1255 }
1256 
1257 void GERBER_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSize,
1258  int aCornerRadius, double aOrient,
1259  OUTLINE_MODE aTraceMode, void* aData )
1260 
1261 {
1262  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1263 
1264  if( aTraceMode != FILLED )
1265  {
1266  SHAPE_POLY_SET outline;
1267  TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient,
1268  aCornerRadius, 0.0, 0, GetPlotterArcHighDef(), ERROR_INSIDE );
1269 
1270  SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata );
1271  outline.Inflate( -GetCurrentLineWidth()/2, 16 );
1272 
1273  std::vector< wxPoint > cornerList;
1274  // TransformRoundRectToPolygon creates only one convex polygon
1275  SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
1276  cornerList.reserve( poly.PointCount() + 1 );
1277 
1278  for( int ii = 0; ii < poly.PointCount(); ++ii )
1279  cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
1280 
1281  // Close polygon
1282  cornerList.push_back( cornerList[0] );
1283 
1284  // plot outlines
1285  PlotPoly( cornerList, FILL_TYPE::NO_FILL, GetCurrentLineWidth(), gbr_metadata );
1286  }
1287  else
1288  {
1289  #ifdef GBR_USE_MACROS_FOR_ROUNDRECT
1291  #endif
1292  {
1293  m_hasApertureRoundRect = true;
1294 
1295  DPOINT pos_dev = userToDeviceCoordinates( aPadPos );
1296  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1297  selectAperture( aSize, aCornerRadius, aOrient/10.0,
1298  APERTURE::AM_ROUND_RECT, aperture_attrib );
1299 
1300  if( gbr_metadata )
1301  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1302 
1303  emitDcode( pos_dev, 3 );
1304  return;
1305  }
1306  // A Pad RoundRect is plotted as a Gerber region.
1307  // Initialize region metadata:
1308  bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
1309 
1310  if( gbr_metadata )
1311  {
1312  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1313  std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
1314 
1315  if( !attrib.empty() )
1316  {
1317  fputs( attrib.c_str(), m_outputFile );
1318  clearTA_AperFunction = true;
1319  }
1320  }
1321 
1322  // Plot the region using arcs in corners.
1323  plotRoundRectAsRegion( aPadPos, aSize, aCornerRadius, aOrient );
1324 
1325  // Clear the TA attribute, to avoid the next item to inherit it:
1326  if( clearTA_AperFunction )
1327  {
1328  if( m_useX2format )
1329  fputs( "%TD.AperFunction*%\n", m_outputFile );
1330  else
1331  fputs( "G04 #@! TD.AperFunction*\n", m_outputFile );
1332  }
1333  }
1334 }
1335 
1336 
1337 void GERBER_PLOTTER::plotRoundRectAsRegion( const wxPoint& aRectCenter, const wxSize& aSize,
1338  int aCornerRadius, double aOrient )
1339 {
1340  // The region outline is generated by 4 sides and 4 90 deg arcs
1341  // 1 --- 2
1342  // | c |
1343  // 4 --- 3
1344 
1345  // Note also in user coordinates the Y axis is from top to bottom
1346  // for historical reasons.
1347 
1348  // A helper structure to handle outlines coordinates (segments and arcs)
1349  // in user coordinates
1350  struct RR_EDGE
1351  {
1352  wxPoint m_start;
1353  wxPoint m_end;
1354  wxPoint m_center;
1355  // in decidegrees: angle start. angle end = m_arc_angle_start+arc_angle
1356  double m_arc_angle_start;
1357  };
1358 
1359  const double arc_angle = -900.0; // in decidegrees
1360  int hsizeX = aSize.x/2;
1361  int hsizeY = aSize.y/2;
1362 
1363  RR_EDGE curr_edge;
1364  std::vector<RR_EDGE> rr_outline;
1365 
1366  // Build outline coordinates, relative to rectangle center, rotation 0:
1367 
1368  // Top left corner 1 (and 4 to 1 left vertical side @ x=-hsizeX)
1369  curr_edge.m_start.x = -hsizeX;
1370  curr_edge.m_start.y = hsizeY - aCornerRadius;
1371  curr_edge.m_end.x = curr_edge.m_start.x;
1372  curr_edge.m_end.y = -hsizeY + aCornerRadius;
1373  curr_edge.m_center.x = -hsizeX + aCornerRadius;
1374  curr_edge.m_center.y = curr_edge.m_end.y;
1375  curr_edge.m_arc_angle_start = aOrient + 1800.0; // En decidegree
1376 
1377  rr_outline.push_back( curr_edge );
1378 
1379  // Top right corner 2 (and 1 to 2 top horizontal side @ y=-hsizeY)
1380  curr_edge.m_start.x = -hsizeX + aCornerRadius;
1381  curr_edge.m_start.y = -hsizeY;
1382  curr_edge.m_end.x = hsizeX - aCornerRadius;
1383  curr_edge.m_end.y = curr_edge.m_start.y;
1384  curr_edge.m_center.x = curr_edge.m_end.x;
1385  curr_edge.m_center.y = -hsizeY + aCornerRadius;
1386  curr_edge.m_arc_angle_start = aOrient + 900.0; // En decidegree
1387 
1388  rr_outline.push_back( curr_edge );
1389 
1390  // bottom right corner 3 (and 2 to 3 right vertical side @ x=hsizeX)
1391  curr_edge.m_start.x = hsizeX;
1392  curr_edge.m_start.y = -hsizeY + aCornerRadius;
1393  curr_edge.m_end.x = curr_edge.m_start.x;
1394  curr_edge.m_end.y = hsizeY - aCornerRadius;
1395  curr_edge.m_center.x = hsizeX - aCornerRadius;
1396  curr_edge.m_center.y = curr_edge.m_end.y;
1397  curr_edge.m_arc_angle_start = aOrient + 0.0; // En decidegree
1398 
1399  rr_outline.push_back( curr_edge );
1400 
1401  // bottom left corner 4 (and 3 to 4 bottom horizontal side @ y=hsizeY)
1402  curr_edge.m_start.x = hsizeX - aCornerRadius;
1403  curr_edge.m_start.y = hsizeY;
1404  curr_edge.m_end.x = -hsizeX + aCornerRadius;
1405  curr_edge.m_end.y = curr_edge.m_start.y;
1406  curr_edge.m_center.x = curr_edge.m_end.x;
1407  curr_edge.m_center.y = hsizeY - aCornerRadius;
1408  curr_edge.m_arc_angle_start = aOrient - 900.0; // En decidegree
1409 
1410  rr_outline.push_back( curr_edge );
1411 
1412  // Move relative coordinates to the actual location and rotation:
1413  wxPoint arc_last_center;
1414  int arc_last_angle = curr_edge.m_arc_angle_start+arc_angle;
1415 
1416  for( RR_EDGE& rr_edge: rr_outline )
1417  {
1418  RotatePoint( &rr_edge.m_start, aOrient );
1419  RotatePoint( &rr_edge.m_end, aOrient );
1420  RotatePoint( &rr_edge.m_center, aOrient );
1421  rr_edge.m_start += aRectCenter;
1422  rr_edge.m_end += aRectCenter;
1423  rr_edge.m_center += aRectCenter;
1424  arc_last_center = rr_edge.m_center;
1425  }
1426 
1427  // Ensure the region is a closed polygon, i.e. the end point of last segment
1428  // (end of arc) is the same as the first point. Rounding issues can create a
1429  // small difference, mainly for rotated pads.
1430  // calculate last point (end of last arc):
1431  wxPoint last_pt;
1432  last_pt.x = arc_last_center.x + KiROUND( cosdecideg( aCornerRadius, arc_last_angle ) );
1433  last_pt.y = arc_last_center.y - KiROUND( sindecideg( aCornerRadius, arc_last_angle ) );
1434 
1435  wxPoint first_pt = rr_outline[0].m_start;
1436 
1437 #if 0 // For test only:
1438  if( last_pt != first_pt )
1439  wxLogMessage( "first pt %d %d last pt %d %d",
1440  first_pt.x, first_pt.y, last_pt.x, last_pt.y );
1441 #endif
1442 
1443  fputs( "G36*\n", m_outputFile ); // Start region
1444  fputs( "G01*\n", m_outputFile ); // Set linear interpolation.
1445  first_pt = last_pt;
1446  MoveTo( first_pt ); // Start point of region, must be same as end point
1447 
1448  for( RR_EDGE& rr_edge: rr_outline )
1449  {
1450  if( aCornerRadius ) // Guard: ensure we do not create arcs with radius = 0
1451  {
1452  // LineTo( rr_edge.m_end ); // made in plotArc()
1453  plotArc( rr_edge.m_center,
1454  rr_edge.m_arc_angle_start, rr_edge.m_arc_angle_start+arc_angle,
1455  aCornerRadius, true );
1456  }
1457  else
1458  LineTo( rr_edge.m_end );
1459  }
1460 
1461  fputs( "G37*\n", m_outputFile ); // Close region
1462 }
1463 
1464 
1465 void GERBER_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize,
1466  double aOrient, SHAPE_POLY_SET* aPolygons,
1467  OUTLINE_MODE aTraceMode, void* aData )
1468 
1469 {
1470  // A Pad custom is plotted as polygon (a region in Gerber language).
1471  GBR_METADATA gbr_metadata;
1472 
1473  if( aData )
1474  gbr_metadata = *static_cast<GBR_METADATA*>( aData );
1475 
1476  SHAPE_POLY_SET polyshape = *aPolygons;
1477 
1478  if( aTraceMode != FILLED )
1479  {
1480  SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata );
1481  polyshape.Inflate( -GetCurrentLineWidth()/2, 16 );
1482  }
1483 
1484  std::vector< wxPoint > cornerList;
1485 
1486  for( int cnt = 0; cnt < polyshape.OutlineCount(); ++cnt )
1487  {
1488  SHAPE_LINE_CHAIN& poly = polyshape.Outline( cnt );
1489 
1490  cornerList.clear();
1491 
1492  for( int ii = 0; ii < poly.PointCount(); ++ii )
1493  cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
1494 
1495  // Close polygon
1496  cornerList.push_back( cornerList[0] );
1497 
1498  if( aTraceMode == SKETCH )
1499  PlotPoly( cornerList, FILL_TYPE::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
1500  else
1501  {
1502 #ifdef GBR_USE_MACROS_FOR_CUSTOM_PAD
1504  || cornerList.size() > GBR_MACRO_FOR_CUSTOM_PAD_MAX_CORNER_COUNT )
1505  PlotGerberRegion( cornerList, &gbr_metadata );
1506  else
1507  {
1508  // An AM will be created. the shape must be in position 0,0 and orientation 0
1509  // to be able to reuse the same AM for pads having the same shape
1510  for( size_t ii = 0; ii < cornerList.size(); ii++ )
1511  {
1512  cornerList[ii] -= aPadPos;
1513  RotatePoint( &cornerList[ii], -aOrient );
1514  }
1515 
1516  DPOINT pos_dev = userToDeviceCoordinates( aPadPos );
1517  selectAperture( cornerList, aOrient/10.0,
1518  APERTURE::AM_FREE_POLYGON, gbr_metadata.GetApertureAttrib() );
1519  formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1520 
1521  emitDcode( pos_dev, 3 );
1522  }
1523 #else
1524  PlotGerberRegion( cornerList, &gbr_metadata );
1525 #endif
1526  }
1527  }
1528 }
1529 
1530 
1531 void GERBER_PLOTTER::FlashPadChamferRoundRect( const wxPoint& aShapePos, const wxSize& aPadSize,
1532  int aCornerRadius, double aChamferRatio,
1533  int aChamferPositions,
1534  double aPadOrient, OUTLINE_MODE aPlotMode, void* aData )
1535 
1536 {
1537  GBR_METADATA gbr_metadata;
1538 
1539  if( aData )
1540  gbr_metadata = *static_cast<GBR_METADATA*>( aData );
1541 
1542  DPOINT pos_dev = userToDeviceCoordinates( aShapePos );
1543 
1544  SHAPE_POLY_SET outline;
1545  // polygon corners list
1546  std::vector<wxPoint> cornerList;
1547 
1548  bool hasRoundedCorner = aCornerRadius != 0 && aChamferPositions != 15;
1549 
1550 #ifdef GBR_USE_MACROS_FOR_CHAMFERED_RECT
1551  // Sketch mode or round rect shape or Apert Macros disabled
1552  if( aPlotMode != FILLED || hasRoundedCorner || m_gerberDisableApertMacros )
1553 #endif
1554  {
1555  TransformRoundChamferedRectToPolygon( outline, aShapePos, aPadSize, aPadOrient,
1556  aCornerRadius, aChamferRatio, aChamferPositions,
1558 
1559  // Build the corner list
1560  const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
1561 
1562  for( int ii = 0; ii < corners.PointCount(); ii++ )
1563  cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
1564 
1565  // Close the polygon
1566  cornerList.push_back( cornerList[0] );
1567 
1568  if( aPlotMode == SKETCH )
1569  PlotPoly( cornerList, FILL_TYPE::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
1570  else
1571  {
1572 #ifdef GBR_USE_MACROS_FOR_CHAMFERED_ROUND_RECT
1574  PlotGerberRegion( cornerList, &gbr_metadata );
1575  else
1576  {
1577  // An AM will be created. the shape must be in position 0,0 and orientation 0
1578  // to be able to reuse the same AM for pads having the same shape
1579  for( size_t ii = 0; ii < cornerList.size(); ii++ )
1580  {
1581  cornerList[ii] -= aShapePos;
1582  RotatePoint( &cornerList[ii], -aPadOrient );
1583  }
1584 
1585  selectAperture( cornerList, aPadOrient/10.0,
1586  APERTURE::AM_FREE_POLYGON, gbr_metadata.GetApertureAttrib() );
1587  formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1588 
1589  emitDcode( pos_dev, 3 );
1590  }
1591 #else
1592  PlotGerberRegion( cornerList, &gbr_metadata );
1593 #endif
1594  }
1595 
1596  return;
1597  }
1598 
1599  // Build the chamfered polygon (4 to 8 corners )
1600  TransformRoundChamferedRectToPolygon( outline, wxPoint( 0, 0 ), aPadSize, 0.0, 0,
1601  aChamferRatio, aChamferPositions,
1603 
1604  // Build the corner list
1605  const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
1606 
1607  // Generate the polygon (4 to 8 corners )
1608  for( int ii = 0; ii < corners.PointCount(); ii++ )
1609  cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
1610 
1611  switch( cornerList.size() )
1612  {
1613  case 4:
1614  m_hasApertureOutline4P = true;
1615  selectAperture( cornerList, aPadOrient/10.0,
1617  break;
1618 
1619  case 5:
1621  selectAperture( cornerList, aPadOrient/10.0,
1623  break;
1624 
1625  case 6:
1627  selectAperture( cornerList, aPadOrient/10.0,
1629  break;
1630 
1631  case 7:
1633  selectAperture( cornerList, aPadOrient/10.0,
1635  break;
1636 
1637  case 8:
1639  selectAperture( cornerList, aPadOrient/10.0,
1641  break;
1642 
1643  default:
1644  wxLogMessage( "FlashPadChamferRoundRect(): Unexpected number of corners (%d)",
1645  (int)cornerList.size() );
1646  break;
1647  }
1648 
1649  formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1650 
1651  emitDcode( pos_dev, 3 );
1652 }
1653 
1654 
1655 void GERBER_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCorners,
1656  double aPadOrient, OUTLINE_MODE aTrace_Mode, void* aData )
1657 
1658 {
1659  // polygon corners list
1660  std::vector<wxPoint> cornerList = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
1661 
1662  // Draw the polygon and fill the interior as required
1663  for( unsigned ii = 0; ii < 4; ii++ )
1664  {
1665  RotatePoint( &cornerList[ii], aPadOrient );
1666  cornerList[ii] += aPadPos;
1667  }
1668 
1669  // Close the polygon
1670  cornerList.push_back( cornerList[0] );
1671  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1672 
1673  GBR_METADATA metadata;
1674 
1675  if( gbr_metadata )
1676  metadata = *gbr_metadata;
1677 
1678  if( aTrace_Mode == SKETCH )
1679  {
1680  PlotPoly( cornerList, FILL_TYPE::NO_FILL, GetCurrentLineWidth(), &metadata );
1681  return;
1682  }
1683 
1684  // Plot a filled polygon:
1685  #ifdef GBR_USE_MACROS_FOR_TRAPEZOID
1687  #endif
1688  {
1689  m_hasApertureOutline4P = true;
1690  DPOINT pos_dev = userToDeviceCoordinates( aPadPos );
1691  // polygon corners list
1692  std::vector<wxPoint> corners = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
1693  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1694  selectAperture( corners, aPadOrient/10.0, APERTURE::APER_MACRO_OUTLINE4P, aperture_attrib );
1695 
1696  if( gbr_metadata )
1697  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1698 
1699  emitDcode( pos_dev, 3 );
1700  return;
1701  }
1702 
1703  PlotGerberRegion( cornerList, &metadata );
1704 }
1705 
1706 
1707 void GERBER_PLOTTER::FlashRegularPolygon( const wxPoint& aShapePos,
1708  int aDiameter, int aCornerCount,
1709  double aOrient, OUTLINE_MODE aTraceMode,
1710  void* aData )
1711 {
1712  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1713 
1714  GBR_METADATA metadata;
1715 
1716  if( gbr_metadata )
1717  metadata = *gbr_metadata;
1718 
1719  if( aTraceMode == SKETCH )
1720  {
1721  // Build the polygon:
1722  std::vector< wxPoint > cornerList;
1723 
1724  double angle_delta = 3600.0 / aCornerCount; // in 0.1 degree
1725 
1726  for( int ii = 0; ii < aCornerCount; ii++ )
1727  {
1728  double rot = aOrient + (angle_delta*ii);
1729  wxPoint vertice( aDiameter/2, 0 );
1730  RotatePoint( &vertice, rot );
1731  vertice += aShapePos;
1732  cornerList.push_back( vertice );
1733  }
1734 
1735  cornerList.push_back( cornerList[0] ); // Close the shape
1736 
1737  PlotPoly( cornerList, FILL_TYPE::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
1738  }
1739  else
1740  {
1741  DPOINT pos_dev = userToDeviceCoordinates( aShapePos );
1742 
1743  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1744 
1745  APERTURE::APERTURE_TYPE apert_type =
1747  selectAperture( aDiameter, aOrient, apert_type, aperture_attrib );
1748 
1749  if( gbr_metadata )
1750  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1751 
1752  emitDcode( pos_dev, 3 );
1753  }
1754 }
1755 
1756 
1757 void GERBER_PLOTTER::Text( const wxPoint& aPos, const COLOR4D aColor,
1758  const wxString& aText, double aOrient, const wxSize& aSize,
1759  enum EDA_TEXT_HJUSTIFY_T aH_justify,
1760  enum EDA_TEXT_VJUSTIFY_T aV_justify, int aWidth, bool aItalic,
1761  bool aBold, bool aMultilineAllowed, void* aData )
1762 {
1763  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1764 
1765  if( gbr_metadata )
1766  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1767 
1768  PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth, aItalic,
1769  aBold, aMultilineAllowed, aData );
1770 }
1771 
1772 
1773 void GERBER_PLOTTER::SetLayerPolarity( bool aPositive )
1774 {
1775  if( aPositive )
1776  fprintf( m_outputFile, "%%LPD*%%\n" );
1777  else
1778  fprintf( m_outputFile, "%%LPC*%%\n" );
1779 }
1780 
1781 
1782 bool APER_MACRO_FREEPOLY::IsSamePoly( const std::vector<wxPoint>& aPolygon ) const
1783 {
1784  return polyCompare( m_Corners, aPolygon );
1785 }
1786 
1787 
1788 void APER_MACRO_FREEPOLY::Format( FILE * aOutput, double aIu2GbrMacroUnit )
1789 {
1790  // Write aperture header
1791  fprintf( aOutput, "%%AM%s%d*\n", AM_FREEPOLY_BASENAME, m_Id );
1792  fprintf( aOutput, "4,1,%d,", (int)m_Corners.size() );
1793 
1794  // Insert a newline after curr_line_count_max coordinates.
1795  int curr_line_corner_count = 0;
1796  const int curr_line_count_max = 20; // <= 0 to disable newlines
1797 
1798  for( size_t ii = 0; ii <= m_Corners.size(); ii++ )
1799  {
1800  int jj = ii;
1801 
1802  if( ii >= m_Corners.size() )
1803  jj = 0;
1804 
1805  // Note: parameter values are always mm or inches
1806  fprintf( aOutput, "%#f,%#f,",
1807  m_Corners[jj].x * aIu2GbrMacroUnit, -m_Corners[jj].y * aIu2GbrMacroUnit );
1808 
1809  if( curr_line_count_max >= 0
1810  && ++curr_line_corner_count >= curr_line_count_max )
1811  {
1812  fprintf( aOutput, "\n" );
1813  curr_line_corner_count = 0;
1814  }
1815  }
1816 
1817  // output rotation parameter
1818  fputs( "$1*%\n", aOutput );
1819 }
1820 
1821 
1822 void APER_MACRO_FREEPOLY_LIST::Format( FILE * aOutput, double aIu2GbrMacroUnit )
1823 {
1824  for( int idx = 0; idx < AmCount(); idx++ )
1825  m_AMList[idx].Format( aOutput, aIu2GbrMacroUnit );
1826 }
1827 
1828 
1829 void APER_MACRO_FREEPOLY_LIST::Append( const std::vector<wxPoint>& aPolygon )
1830 {
1831  m_AMList.emplace_back( aPolygon, AmCount() );
1832 }
1833 
1834 
1835 int APER_MACRO_FREEPOLY_LIST::FindAm( const std::vector<wxPoint>& aPolygon ) const
1836 {
1837  for( int idx = 0; idx < AmCount(); idx++ )
1838  {
1839  if( m_AMList[idx].IsSamePoly( aPolygon ) )
1840  return idx;
1841  }
1842 
1843  return -1;
1844 }
void segmentAsOval(const wxPoint &start, const wxPoint &end, int width, OUTLINE_MODE tracemode)
Convert a thick segment and plot it as an oval.
Definition: plotter.cpp:437
virtual void FlashRegularPolygon(const wxPoint &aShapePos, int aDiameter, int aCornerCount, double aOrient, OUTLINE_MODE aTraceMode, void *aData) override
Flash a regular polygon.
void FinishTo(const wxPoint &pos)
Definition: plotter.h:267
Handle special data (items attributes) during plot.
#define APER_MACRO_ROUNDRECT_NAME
#define APER_MACRO_ROT_RECT_HEADER
specialized plotter for GERBER files format
OUTLINE_MODE
Definition: outline_mode.h:24
void writeApertureList()
Generate the table of D codes.
void clearNetAttribute()
clear a Gerber net attribute record (clear object attribute dictionary) and output the clear object a...
EDA_TEXT_HJUSTIFY_T
Definition: eda_text.h:61
virtual void PenTo(const wxPoint &pos, char plume) override
moveto/lineto primitive, moves the 'pen' to the specified direction
void PenFinish()
Definition: plotter.h:273
int OutlineCount() const
Return the number of vertices in a given outline/hole.
void plotRoundRectAsRegion(const wxPoint &aRectCenter, const wxSize &aSize, int aCornerRadius, double aOrient)
Plot a round rect (a round rect shape in fact) as a Gerber region using lines and arcs for corners.
FILL_TYPE
The set of fill types used in plotting or drawing enclosed areas.
Definition: fill_type.h:28
virtual void SetLayerPolarity(bool aPositive) override
Change the plot polarity and begin a new layer Used to 'scratch off' silk screen away from solder mas...
virtual void EndBlock(void *aData) override
calling this function allows one to define the end of a group of drawing items the group is started b...
bool m_hasApertureChamferedRect
APERTURE_TYPE m_Type
virtual void ThickSegment(const wxPoint &start, const wxPoint &end, int width, OUTLINE_MODE tracemode, void *aData) override
void FlashPadChamferRoundRect(const wxPoint &aShapePos, const wxSize &aPadSize, int aCornerRadius, double aChamferRatio, int aChamferPositions, double aPadOrient, OUTLINE_MODE aPlotMode, void *aData)
flash a chamfered round rect pad.
#define APER_MACRO_SHAPE_OVAL_NAME
void formatNetAttribute(GBR_NETLIST_METADATA *aData)
print a Gerber net attribute object record.
virtual void FlashPadTrapez(const wxPoint &aPadPos, const wxPoint *aCorners, double aPadOrient, OUTLINE_MODE aTraceMode, void *aData) override
Flash a trapezoidal pad.
static bool polyCompare(const std::vector< wxPoint > &aPolygon, const std::vector< wxPoint > &aTestPolygon)
FILE * m_outputFile
Output file.
Definition: plotter.h:580
double m_iuPerDeviceUnit
Definition: plotter.h:571
void TransformRoundChamferedRectToPolygon(SHAPE_POLY_SET &aCornerBuffer, const wxPoint &aPosition, const wxSize &aSize, double aRotation, int aCornerRadius, double aChamferRatio, int aChamferCorners, int aError, ERROR_LOC aErrorLoc)
convert a rectangle with rounded corners and/or chamfered corners to a polygon Convert rounded corner...
void plotArc(const wxPoint &aCenter, double aStAngle, double aEndAngle, int aRadius, bool aPlotInRegion)
Plot a Gerber arc.
std::string m_objectAttributesDictionary
virtual void ThickArc(const wxPoint &centre, double StAngle, double EndAngle, int rayon, int width, OUTLINE_MODE tracemode, void *aData) override
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
#define APER_MACRO_OUTLINE7P_NAME
APER_MACRO_FREEPOLY_LIST m_am_freepoly_list
Information which can be added in a gerber file as attribute of an object.
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:228
double m_IUsPerDecimil
Definition: plotter.h:569
void Inflate(int aAmount, int aCircleSegmentsCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
Perform outline inflation/deflation.
int PointCount() const
Function PointCount()
wxArrayString m_headerExtraLines
Definition: plotter.h:595
virtual void FlashPadRect(const wxPoint &aPadPos, const wxSize &size, double orient, OUTLINE_MODE trace_mode, void *aData) override
#define APER_MACRO_OUTLINE6P_HEADER
bool m_hasApertureOutline4P
virtual bool StartPlot() override
Function StartPlot Write GERBER header to file initialize global variable g_Plot_PlotOutputFile.
void Append(const std::vector< wxPoint > &aPolygon)
append a new APER_MACRO_FREEPOLY containing the polygon aPolygon to the current list
This file contains miscellaneous commonly used macros and functions.
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.
virtual void Text(const wxPoint &aPos, const COLOR4D aColor, const wxString &aText, double aOrient, const wxSize &aSize, enum EDA_TEXT_HJUSTIFY_T aH_justify, enum EDA_TEXT_VJUSTIFY_T aV_justify, int aWidth, bool aItalic, bool aBold, bool aMultilineAllowed=false, void *aData=NULL)
Draws text with the plotter.
Definition: gr_text.cpp:219
bool m_hasApertureRoundRect
double m_plotScale
Plot scale - chosen by the user (even implicitly with 'fit in a4')
Definition: plotter.h:563
virtual void Text(const wxPoint &aPos, const COLOR4D aColor, const wxString &aText, double aOrient, const wxSize &aSize, enum EDA_TEXT_HJUSTIFY_T aH_justify, enum EDA_TEXT_VJUSTIFY_T aV_justify, int aWidth, bool aItalic, bool aBold, bool aMultilineAllowed=false, void *aData=NULL) override
Draws text with the plotter.
virtual int GetCurrentLineWidth() const
Definition: plotter.h:166
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
virtual void PlotPoly(const std::vector< wxPoint > &aCornerList, FILL_TYPE aFill, int aWidth=USE_DEFAULT_LINE_WIDTH, void *aData=nullptr) override
Gerber polygon: they can (and should) be filled with the appropriate G36/G37 sequence.
const VECTOR2I & CPoint(int aIndex) const
Function Point()
virtual void FlashPadCustom(const wxPoint &aPadPos, const wxSize &aSize, double aPadOrient, SHAPE_POLY_SET *aPolygons, OUTLINE_MODE aTraceMode, void *aData) override
wxString m_ExtraData
a string to print after TO object attributes, if not empty it is printed "as this"
void LineTo(const wxPoint &pos)
Definition: plotter.h:262
static const int USE_DEFAULT_LINE_WIDTH
Definition: plotter.h:126
std::vector< APERTURE > m_apertures
bool m_plotMirror
Definition: plotter.h:574
int m_currentPenWidth
Definition: plotter.h:585
#define APER_MACRO_SHAPE_OVAL_HEADER
#define APER_MACRO_OUTLINE5P_HEADER
virtual void Arc(const wxPoint &aCenter, double aStAngle, double aEndAngle, int aRadius, FILL_TYPE aFill, int aWidth=USE_DEFAULT_LINE_WIDTH) override
Generic fallback: arc rendered as a polyline.
void ClearAllAttributes()
Remove (clear) all attributes from object attributes dictionary (TO.
virtual void SetCurrentLineWidth(int width, void *aData=NULL) override
Set the line width for the next drawing.
wxString GetBuildVersion()
Get the full KiCad version string.
virtual void FlashPadRoundRect(const wxPoint &aPadPos, const wxSize &aSize, int aCornerRadius, double aOrient, OUTLINE_MODE aTraceMode, void *aData) override
virtual void ThickCircle(const wxPoint &pos, int diametre, int width, OUTLINE_MODE tracemode, void *aData) override
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
virtual void Circle(const wxPoint &pos, int diametre, FILL_TYPE fill, int width=USE_DEFAULT_LINE_WIDTH) override
std::vector< wxPoint > m_Corners
Base window classes and related definitions.
virtual void SetViewport(const wxPoint &aOffset, double aIusPerDecimil, double aScale, bool aMirror) override
Set the plot offset and scaling for the current plot.
#define APER_MACRO_ROUNDRECT_HEADER
GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB GetApertureAttrib()
Definition: gbr_metadata.h:214
#define APER_MACRO_OUTLINE5P_NAME
RENDER_SETTINGS * m_renderSettings
Definition: plotter.h:597
Metadata which can be added in a gerber file as attribute in X2 format.
Definition: gbr_metadata.h:204
#define APER_MACRO_OUTLINE8P_NAME
virtual void FlashPadOval(const wxPoint &aPadPos, const wxSize &size, double orient, OUTLINE_MODE trace_mode, void *aData) override
#define APER_MACRO_ROT_RECT_NAME
void emitDcode(const DPOINT &pt, int dcode)
Emit a D-Code record, using proper conversions to format a leading zero omitted gerber coordinate (fo...
std::vector< APER_MACRO_FREEPOLY > m_AMList
#define AM_FREEPOLY_BASENAME
void selectAperture(const wxSize &aSize, int aRadius, double aRotDegree, APERTURE::APERTURE_TYPE aType, int aApertureAttribute)
Pick an existing aperture or create a new one, matching the size, type and attributes.
EDA_TEXT_VJUSTIFY_T
Definition: eda_text.h:68
Plotting engine (Gerber)
void MoveTo(const wxPoint &pos)
Definition: plotter.h:257
virtual DPOINT userToDeviceCoordinates(const wxPoint &aCoordinate)
Modifies coordinates according to the orientation, scale factor, and offsets trace.
Definition: plotter.cpp:93
static std::string FormatAttribute(GBR_APERTURE_ATTRIB aAttribute, bool aUseX1StructuredComment)
double cosdecideg(double r, double a)
Circle generation utility: computes r * cos(a) Where a is in decidegrees, not in radians.
Definition: trigo.h:439
#define APER_MACRO_OUTLINE4P_NAME
virtual void ThickRect(const wxPoint &p1, const wxPoint &p2, int width, OUTLINE_MODE tracemode, void *aData) override
#define APER_MACRO_OUTLINE7P_HEADER
double sindecideg(double r, double a)
Circle generation utility: computes r * sin(a) Where a is in decidegrees, not in radians.
Definition: trigo.h:430
std::vector< wxPoint > m_Corners
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...
wxPoint m_plotOffset
Definition: plotter.h:573
int FindAm(const std::vector< wxPoint > &aPolygon) const
int GetOrCreateAperture(const wxSize &aSize, int aRadius, double aRotDegree, APERTURE::APERTURE_TYPE aType, int aApertureAttribute)
#define APER_MACRO_OUTLINE6P_NAME
virtual void Rect(const wxPoint &p1, const wxPoint &p2, FILL_TYPE fill, int width=USE_DEFAULT_LINE_WIDTH) override
void PlotGerberRegion(const std::vector< wxPoint > &aCornerList, void *aData=NULL)
Plot a Gerber region: similar to PlotPoly but plot only filled polygon, and add the TA....
virtual bool EndPlot() override
SHAPE_LINE_CHAIN.
#define APER_MACRO_OUTLINE4P_HEADER
void Format(FILE *aOutput, double aIu2GbrMacroUnit)
print the aperture macro definition to aOutput
#define GBR_MACRO_FOR_CUSTOM_PAD_MAX_CORNER_COUNT
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:68
int GetPlotterArcHighDef() const
Definition: plotter.h:223
char m_penState
Definition: plotter.h:586
virtual void SetGerberCoordinatesFormat(int aResolution, bool aUseInches=false) override
Function SetGerberCoordinatesFormat selection of Gerber units and resolution (number of digits in man...
int GetDefaultPenWidth() const
#define APER_MACRO_OUTLINE8P_HEADER
wxString m_workFilename
virtual void FlashPadCircle(const wxPoint &pos, int diametre, OUTLINE_MODE trace_mode, void *aData) override
Filled circular flashes are stored as apertures.
void Format(FILE *aOutput, double aIu2GbrMacroUnit)
print the aperture macro list to aOutput
GBR_NETLIST_METADATA m_NetlistMetadata
An item to handle object attribute.
Definition: gbr_metadata.h:262
virtual void FilledCircle(const wxPoint &pos, int diametre, OUTLINE_MODE tracemode, void *aData) override
void SetDefaultPenWidth(int aWidth)
bool m_gerberDisableApertMacros
bool IsSamePoly(const std::vector< wxPoint > &aPolygon) const
static const int DO_NOT_SET_LINE_WIDTH
Definition: plotter.h:125
wxSize m_paperSize
Definition: plotter.h:593
void sketchOval(const wxPoint &pos, const wxSize &size, double orient, int width)
Definition: plotter.cpp:458
wxString m_creator
Definition: plotter.h:589
GBR_APERTURE_METADATA m_ApertureMetadata
An item to handle aperture attribute.
Definition: gbr_metadata.h:257
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:98