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 #include <wx/log.h>
35 
36 #include <build_version.h>
37 
38 #include "plotter_gerber.h"
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
74 static bool polyCompare( const std::vector<wxPoint>& aPolygon,
75  const std::vector<wxPoint>& 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 
124 void GERBER_PLOTTER::SetViewport( const wxPoint& 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 
146 void 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 
158 void GERBER_PLOTTER::emitDcode( const DPOINT& pt, int dcode )
159 {
160 
161  fprintf( m_outputFile, "X%dY%dD%02d*\n", KiROUND( pt.x ), KiROUND( pt.y ), dcode );
162 }
163 
165 {
166  // Remove all attributes from object attributes dictionary (TO. and TA commands)
167  if( m_useX2format )
168  fputs( "%TD*%\n", m_outputFile );
169  else
170  fputs( "G04 #@! TD*\n", m_outputFile );
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  fputs( "%TD*%\n", m_outputFile );
185  else
186  fputs( "G04 #@! TD*\n", m_outputFile );
187 
189 }
190 
191 
192 void 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 
199 void 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  fputs( short_attribute_string.c_str(), m_outputFile );
231 
232  if( m_useX2format && !aData->m_ExtraData.IsEmpty() )
233  {
234  std::string extra_data = TO_UTF8( aData->m_ExtraData );
235  fputs( extra_data.c_str(), m_outputFile );
236  }
237 }
238 
239 
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
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  fprintf( m_outputFile, "%s\n", 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  fprintf( m_outputFile, "%%FSLAX%d%dY%d%d*%%\n",
277  leadingDigitCount, m_gerberUnitFmt,
278  leadingDigitCount, m_gerberUnitFmt );
279  fprintf( m_outputFile,
280  "G04 Gerber Fmt %d.%d, Leading zero omitted, Abs format (unit %s)*\n",
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  fprintf( m_outputFile, "G04 Created by KiCad (%s) date %s*\n",
291  TO_UTF8( Title ), TO_UTF8( date.FormatISOCombined( ' ') ) );
292 
293  /* Mass parameter: unit = INCHES/MM */
294  if( m_gerberUnitInch )
295  fputs( "%MOIN*%\n", m_outputFile );
296  else
297  fputs( "%MOMM*%\n", m_outputFile );
298 
299  // Be sure the usual dark polarity is selected:
300  fputs( "%LPD*%\n", m_outputFile );
301 
302  // Set initial interpolation mode: always G01 (linear):
303  fputs( "G01*\n", m_outputFile );
304 
305  // Add aperture list start point
306  fputs( "G04 APERTURE LIST*\n", m_outputFile );
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
313  pen_min ) );
314  }
315 
316  return true;
317 }
318 
319 
321 {
322  char line[1024];
323  wxString msg;
324 
325  wxASSERT( m_outputFile );
326 
327  /* Outfile is actually a temporary file i.e. workFile */
328  fputs( "M02*\n", m_outputFile );
329  fflush( m_outputFile );
330 
331  fclose( workFile );
332  workFile = wxFopen( m_workFilename, wxT( "rt" ));
333  wxASSERT( workFile );
335 
336  // Placement of apertures in RS274X
337  while( fgets( line, 1024, workFile ) )
338  {
339  fputs( line, m_outputFile );
340 
341  char* substr = strtok( line, "\n\r" );
342 
343  if( substr && strcmp( substr, "G04 APERTURE LIST*" ) == 0 )
344  {
345  // Add aperture list macro:
349  {
350  fputs( "G04 Aperture macros list*\n", m_outputFile );
351 
354 
357 
360 
363 
365  {
370  }
371 
373  {
374  // aperture sizes are in inch or mm, regardless the
375  // coordinates format
376  double fscale = 0.0001 * m_plotScale / m_IUsPerDecimil; // inches
377 
378  if(! m_gerberUnitInch )
379  fscale *= 25.4; // size in mm
380 
382  }
383 
384  fputs( "G04 Aperture macros list end*\n", m_outputFile );
385  }
386 
388  fputs( "G04 APERTURE END LIST*\n", m_outputFile );
389  }
390  }
391 
392  fclose( workFile );
393  fclose( finalFile );
394  ::wxRemoveFile( m_workFilename );
395  m_outputFile = nullptr;
396 
397  return true;
398 }
399 
400 
401 void GERBER_PLOTTER::SetCurrentLineWidth( int aWidth, void* aData )
402 {
403  if( aWidth == DO_NOT_SET_LINE_WIDTH )
404  return;
405  else if( aWidth == USE_DEFAULT_LINE_WIDTH )
407 
408  wxASSERT_MSG( aWidth >= 0, "Plotter called to set negative pen width" );
409 
410  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
411  int aperture_attribute = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
412 
413  selectAperture( wxSize( aWidth, aWidth ), 0, 0.0, APERTURE::AT_PLOTTING, aperture_attribute );
414  m_currentPenWidth = aWidth;
415 }
416 
417 
418 int GERBER_PLOTTER::GetOrCreateAperture( const wxSize& aSize, int aRadius, double aRotDegree,
419  APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
420 {
421  int last_D_code = 9;
422 
423  // Search an existing aperture
424  for( int idx = 0; idx < (int)m_apertures.size(); ++idx )
425  {
426  APERTURE* tool = &m_apertures[idx];
427  last_D_code = tool->m_DCode;
428 
429  if( (tool->m_Type == aType) && (tool->m_Size == aSize) &&
430  (tool->m_Radius == aRadius) && (tool->m_Rotation == aRotDegree) &&
431  (tool->m_ApertureAttribute == aApertureAttribute) )
432  return idx;
433  }
434 
435  // Allocate a new aperture
436  APERTURE new_tool;
437  new_tool.m_Size = aSize;
438  new_tool.m_Type = aType;
439  new_tool.m_Radius = aRadius;
440  new_tool.m_Rotation = aRotDegree;
441  new_tool.m_DCode = last_D_code + 1;
442  new_tool.m_ApertureAttribute = aApertureAttribute;
443 
444  m_apertures.push_back( new_tool );
445 
446  return m_apertures.size() - 1;
447 }
448 
449 
450 int GERBER_PLOTTER::GetOrCreateAperture( const std::vector<wxPoint>& aCorners, double aRotDegree,
451  APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
452 {
453  int last_D_code = 9;
454 
455  // For APERTURE::AM_FREE_POLYGON aperture macros, we need to create the macro
456  // on the fly, because due to the fact the vertex count is not a constant we
457  // cannot create a static definition.
458  if( APERTURE::AM_FREE_POLYGON == aType )
459  {
460  int idx = m_am_freepoly_list.FindAm( aCorners );
461 
462  if( idx < 0 )
463  m_am_freepoly_list.Append( aCorners );
464  }
465 
466  // Search an existing aperture
467  for( int idx = 0; idx < (int)m_apertures.size(); ++idx )
468  {
469  APERTURE* tool = &m_apertures[idx];
470 
471  last_D_code = tool->m_DCode;
472 
473  if( (tool->m_Type == aType) &&
474  (tool->m_Corners.size() == aCorners.size() ) &&
475  (tool->m_Rotation == aRotDegree) &&
476  (tool->m_ApertureAttribute == aApertureAttribute) )
477  {
478  // A candidate is found. the corner lists must be similar
479  bool is_same = polyCompare( tool->m_Corners, aCorners );
480 
481  if( is_same )
482  return idx;
483  }
484  }
485 
486  // Allocate a new aperture
487  APERTURE new_tool;
488 
489  new_tool.m_Corners = aCorners;
490  new_tool.m_Size = wxSize( 0, 0 ); // Not used
491  new_tool.m_Type = aType;
492  new_tool.m_Radius = 0; // Not used
493  new_tool.m_Rotation = aRotDegree;
494  new_tool.m_DCode = last_D_code + 1;
495  new_tool.m_ApertureAttribute = aApertureAttribute;
496 
497  m_apertures.push_back( new_tool );
498 
499  return m_apertures.size() - 1;
500 }
501 
502 
503 void GERBER_PLOTTER::selectAperture( const wxSize& aSize, int aRadius, double aRotDegree,
504  APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
505 {
506  bool change = ( m_currentApertureIdx < 0 ) ||
507  ( m_apertures[m_currentApertureIdx].m_Type != aType ) ||
508  ( m_apertures[m_currentApertureIdx].m_Size != aSize ) ||
509  ( m_apertures[m_currentApertureIdx].m_Radius != aRadius ) ||
510  ( m_apertures[m_currentApertureIdx].m_Rotation != aRotDegree );
511 
512  if( !change )
513  change = m_apertures[m_currentApertureIdx].m_ApertureAttribute != aApertureAttribute;
514 
515  if( change )
516  {
517  // Pick an existing aperture or create a new one
518  m_currentApertureIdx = GetOrCreateAperture( aSize, aRadius, aRotDegree,
519  aType, aApertureAttribute );
520  fprintf( m_outputFile, "D%d*\n", m_apertures[m_currentApertureIdx].m_DCode );
521  }
522 }
523 
524 
525 void GERBER_PLOTTER::selectAperture( const std::vector<wxPoint>& aCorners, double aRotDegree,
526  APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
527 {
528  bool change = ( m_currentApertureIdx < 0 ) ||
529  ( m_apertures[m_currentApertureIdx].m_Type != aType ) ||
530  ( m_apertures[m_currentApertureIdx].m_Corners.size() != aCorners.size() ) ||
531  ( m_apertures[m_currentApertureIdx].m_Rotation != aRotDegree );
532 
533  if( !change ) // Compare corner lists
534  {
535  for( size_t ii = 0; ii < aCorners.size(); ii++ )
536  {
537  if( aCorners[ii] != m_apertures[m_currentApertureIdx].m_Corners[ii] )
538  {
539  change = true;
540  break;
541  }
542  }
543  }
544 
545  if( !change )
546  change = m_apertures[m_currentApertureIdx].m_ApertureAttribute != aApertureAttribute;
547 
548  if( change )
549  {
550  // Pick an existing aperture or create a new one
551  m_currentApertureIdx = GetOrCreateAperture( aCorners, aRotDegree,
552  aType, aApertureAttribute );
553  fprintf( m_outputFile, "D%d*\n", m_apertures[m_currentApertureIdx].m_DCode );
554  }
555 }
556 
557 
558 void GERBER_PLOTTER::selectAperture( int aDiameter, double aPolygonRotation,
559  APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
560 {
561  // Pick an existing aperture or create a new one, matching the
562  // aDiameter, aPolygonRotation, type and attributes for type =
563  // AT_REGULAR_POLY3 to AT_REGULAR_POLY12
564 
565  wxASSERT( aType>= APERTURE::APERTURE_TYPE::AT_REGULAR_POLY3 &&
566  aType <= APERTURE::APERTURE_TYPE::AT_REGULAR_POLY12 );
567 
568  wxSize size( aDiameter, (int)( aPolygonRotation * 1000.0 ) );
569  selectAperture( wxSize( 0, 0), aDiameter/2, aPolygonRotation, aType, aApertureAttribute );
570 }
571 
573 {
574  wxASSERT( m_outputFile );
575  char cbuf[1024];
576  std::string buffer;
577 
578  bool useX1StructuredComment = false;
579 
580  if( !m_useX2format )
581  useX1StructuredComment = true;
582 
583  // Init
584  for( APERTURE& tool : m_apertures )
585  {
586  // aperture sizes are in inch or mm, regardless the
587  // coordinates format
588  double fscale = 0.0001 * m_plotScale / m_IUsPerDecimil; // inches
589 
590  if(! m_gerberUnitInch )
591  fscale *= 25.4; // size in mm
592 
593  int attribute = tool.m_ApertureAttribute;
594 
595  if( attribute != m_apertureAttribute )
596  {
599  useX1StructuredComment ).c_str(), m_outputFile );
600  }
601 
602  sprintf( cbuf, "%%ADD%d", tool.m_DCode );
603  buffer = cbuf;
604 
605  /* Please note: the Gerber specs for mass parameters say that
606  exponential syntax is *not* allowed and the decimal point should
607  also be always inserted. So the %g format is ruled out, but %f is fine
608  (the # modifier forces the decimal point). Sadly the %f formatter
609  can't remove trailing zeros but that's not a problem, since nothing
610  forbid it (the file is only slightly longer) */
611 
612  switch( tool.m_Type )
613  {
614  case APERTURE::AT_CIRCLE:
615  sprintf( cbuf, "C,%#f*%%\n", tool.GetDiameter() * fscale );
616  break;
617 
618  case APERTURE::AT_RECT:
619  sprintf( cbuf, "R,%#fX%#f*%%\n", tool.m_Size.x * fscale,
620  tool.m_Size.y * fscale );
621  break;
622 
624  sprintf( cbuf, "C,%#f*%%\n", tool.m_Size.x * fscale );
625  break;
626 
627  case APERTURE::AT_OVAL:
628  sprintf( cbuf, "O,%#fX%#f*%%\n", tool.m_Size.x * fscale,
629  tool.m_Size.y * fscale );
630  break;
631 
643  sprintf( cbuf, "P,%#fX%dX%#f*%%\n", tool.GetDiameter() * fscale,
644  tool.GetRegPolyVerticeCount(), tool.GetRotation() );
645  break;
646 
647  case APERTURE::AM_ROUND_RECT: // Aperture macro for round rect pads
648  {
649  // The aperture macro needs coordinates of the centers of the 4 corners
650  std::vector<VECTOR2I> corners;
651  wxSize half_size( tool.m_Size.x/2-tool.m_Radius, tool.m_Size.y/2-tool.m_Radius );
652 
653  corners.emplace_back( -half_size.x, -half_size.y );
654  corners.emplace_back( half_size.x, -half_size.y );
655  corners.emplace_back( half_size.x, half_size.y );
656  corners.emplace_back( -half_size.x, half_size.y );
657 
658  // Rotate the corner coordinates:
659  for( int ii = 0; ii < 4; ii++ )
660  RotatePoint( corners[ii], -tool.m_Rotation*10.0 );
661 
662  sprintf( cbuf, "%s,%#fX", APER_MACRO_ROUNDRECT_NAME,
663  tool.m_Radius * fscale );
664  buffer += cbuf;
665 
666  // Add each corner
667  for( int ii = 0; ii < 4; ii++ )
668  {
669  sprintf( cbuf, "%#fX%#fX",
670  corners[ii].x * fscale, corners[ii].y * fscale );
671  buffer += cbuf;
672  }
673 
674  sprintf( cbuf, "0*%%\n" );
675  }
676  break;
677 
678  case APERTURE::AM_ROT_RECT: // Aperture macro for rotated rect pads
679  sprintf( cbuf, "%s,%#fX%#fX%#f*%%\n", APER_MACRO_ROT_RECT_NAME,
680  tool.m_Size.x * fscale, tool.m_Size.y * fscale,
681  tool.m_Rotation );
682  break;
683 
684  case APERTURE::APER_MACRO_OUTLINE4P: // Aperture macro for trapezoid pads
685  case APERTURE::APER_MACRO_OUTLINE5P: // Aperture macro for chamfered rect pads
686  case APERTURE::APER_MACRO_OUTLINE6P: // Aperture macro for chamfered rect pads
687  case APERTURE::APER_MACRO_OUTLINE7P: // Aperture macro for chamfered rect pads
688  case APERTURE::APER_MACRO_OUTLINE8P: // Aperture macro for chamfered rect pads
689  switch( tool.m_Type )
690  {
692  sprintf( cbuf, "%s,", APER_MACRO_OUTLINE4P_NAME ); break;
694  sprintf( cbuf, "%s,", APER_MACRO_OUTLINE5P_NAME ); break;
696  sprintf( cbuf, "%s,", APER_MACRO_OUTLINE6P_NAME ); break;
698  sprintf( cbuf, "%s,", APER_MACRO_OUTLINE7P_NAME ); break;
700  sprintf( cbuf, "%s,", APER_MACRO_OUTLINE8P_NAME ); break;
701  default:
702  break;
703  }
704 
705  buffer += cbuf;
706 
707  // Output all corners (should be 4 to 8 corners)
708  // Remember: the Y coordinate must be negated, due to the fact in Pcbnew
709  // the Y axis is from top to bottom
710  for( size_t ii = 0; ii < tool.m_Corners.size(); ii++ )
711  {
712  sprintf( cbuf, "%#fX%#fX",
713  tool.m_Corners[ii].x * fscale, -tool.m_Corners[ii].y * fscale );
714  buffer += cbuf;
715  }
716 
717  // close outline and output rotation
718  sprintf( cbuf, "%#f*%%\n", tool.m_Rotation );
719  break;
720 
721  case APERTURE::AM_ROTATED_OVAL: // Aperture macro for rotated oval pads
722  // (not rotated is a primitive)
723  // m_Size.x = full length; m_Size.y = width, and the macro aperture expects
724  // the position of ends
725  {
726  // the seg_len is the distance between the 2 circle centers
727  int seg_len = tool.m_Size.x - tool.m_Size.y;
728  // Center of the circle on the segment start point:
729  VECTOR2I start( seg_len/2, 0 );
730  // Center of the circle on the segment end point:
731  VECTOR2I end( - seg_len/2, 0 );
732 
733  RotatePoint( start, tool.m_Rotation*10.0 );
734  RotatePoint( end, tool.m_Rotation*10.0 );
735 
736  sprintf( cbuf, "%s,%#fX%#fX%#fX%#fX%#fX0*%%\n", APER_MACRO_SHAPE_OVAL_NAME,
737  tool.m_Size.y * fscale, // width
738  start.x * fscale, -start.y * fscale, // X,Y corner start pos
739  end.x * fscale, -end.y * fscale ); // X,Y cornerend pos
740  }
741  break;
742 
744  {
745  // Find the aperture macro name in the list of aperture macro
746  // created on the fly for this polygon:
747  int idx = m_am_freepoly_list.FindAm( tool.m_Corners );
748 
749  // Write DCODE id ( "%ADDxx" is already in buffer) and rotation
750  // the full line is something like :%ADD12FreePoly1,45.000000*%
751  sprintf( cbuf, "%s%d,%#f*%%\n", AM_FREEPOLY_BASENAME, idx, tool.m_Rotation );
752  break;
753  }
754  }
755 
756  buffer += cbuf;
757  fputs( buffer.c_str(), m_outputFile );
758 
759  m_apertureAttribute = attribute;
760 
761  // Currently reset the aperture attribute. Perhaps a better optimization
762  // is to store the last attribute
763  if( attribute )
764  {
765  if( m_useX2format )
766  fputs( "%TD*%\n", m_outputFile );
767  else
768  fputs( "G04 #@! TD*\n", m_outputFile );
769 
771  }
772 
773  }
774 }
775 
776 
777 void GERBER_PLOTTER::PenTo( const wxPoint& aPos, char plume )
778 {
779  wxASSERT( m_outputFile );
780  DPOINT pos_dev = userToDeviceCoordinates( aPos );
781 
782  switch( plume )
783  {
784  case 'Z':
785  break;
786 
787  case 'U':
788  emitDcode( pos_dev, 2 );
789  break;
790 
791  case 'D':
792  emitDcode( pos_dev, 1 );
793  }
794 
795  m_penState = plume;
796 }
797 
798 
799 void GERBER_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_TYPE fill, int width )
800 {
801  std::vector< wxPoint > cornerList;
802 
803  // Build corners list
804  cornerList.push_back( p1 );
805  wxPoint corner(p1.x, p2.y);
806  cornerList.push_back( corner );
807  cornerList.push_back( p2 );
808  corner.x = p2.x;
809  corner.y = p1.y;
810  cornerList.push_back( corner );
811  cornerList.push_back( p1 );
812 
813  PlotPoly( cornerList, fill, width );
814 }
815 
816 
817 void GERBER_PLOTTER::Circle( const wxPoint& aCenter, int aDiameter, FILL_TYPE aFill, int aWidth )
818 {
819  Arc( aCenter, 0, 3600, aDiameter / 2, aFill, aWidth );
820 }
821 
822 
823 
824 void GERBER_PLOTTER::Arc( const wxPoint& aCenter, double aStAngle, double aEndAngle,
825  int aRadius, FILL_TYPE aFill, int aWidth )
826 {
827  SetCurrentLineWidth( aWidth );
828 
829  // aFill is not used here.
830  plotArc( aCenter, aStAngle, aEndAngle, aRadius, false );
831 }
832 
833 
834 void GERBER_PLOTTER::Arc( const SHAPE_ARC& aArc )
835 {
836  SetCurrentLineWidth( aArc.GetWidth() );
837 
838  // aFill is not used here.
839  plotArc( aArc, false );
840 }
841 
842 
843 void GERBER_PLOTTER::plotArc( const SHAPE_ARC& aArc, bool aPlotInRegion )
844 {
845  wxPoint start( aArc.GetP0() );
846  wxPoint end( aArc.GetP1() );
847  wxPoint center( aArc.GetCenter() );
848  double start_angle = aArc.GetStartAngle();
849  double end_angle = aArc.GetEndAngle();
850 
851  if( !aPlotInRegion )
852  MoveTo( start);
853  else
854  LineTo( start );
855 
856  DPOINT devEnd = userToDeviceCoordinates( end );
857  DPOINT devCenter = userToDeviceCoordinates( center ) - userToDeviceCoordinates( start );
858 
859  if( start_angle < end_angle )
860  fprintf( m_outputFile, "G03*\n" ); // Active circular interpolation, CCW
861  else
862  fprintf( m_outputFile, "G02*\n" ); // Active circular interpolation, CW
863 
864  fprintf( m_outputFile, "X%dY%dI%dJ%dD01*\n",
865  KiROUND( devEnd.x ), KiROUND( devEnd.y ),
866  KiROUND( devCenter.x ), KiROUND( devCenter.y ) );
867 
868  fprintf( m_outputFile, "G01*\n" ); // Back to linear interpolate (perhaps useless here).
869 }
870 
871 
872 void GERBER_PLOTTER::plotArc( const wxPoint& aCenter, double aStAngle, double aEndAngle,
873  int aRadius, bool aPlotInRegion )
874 {
875  wxPoint start, end;
876  start.x = aCenter.x + KiROUND( cosdecideg( aRadius, aStAngle ) );
877  start.y = aCenter.y - KiROUND( sindecideg( aRadius, aStAngle ) );
878 
879  if( !aPlotInRegion )
880  MoveTo( start );
881  else
882  LineTo( start );
883 
884  end.x = aCenter.x + KiROUND( cosdecideg( aRadius, aEndAngle ) );
885  end.y = aCenter.y - KiROUND( sindecideg( aRadius, aEndAngle ) );
886  DPOINT devEnd = userToDeviceCoordinates( end );
887  DPOINT devCenter = userToDeviceCoordinates( aCenter ) - userToDeviceCoordinates( start );
888 
889  fprintf( m_outputFile, "G75*\n" ); // Multiquadrant (360 degrees) mode
890 
891  if( aStAngle < aEndAngle )
892  fprintf( m_outputFile, "G03*\n" ); // Active circular interpolation, CCW
893  else
894  fprintf( m_outputFile, "G02*\n" ); // Active circular interpolation, CW
895 
896  fprintf( m_outputFile, "X%dY%dI%dJ%dD01*\n",
897  KiROUND( devEnd.x ), KiROUND( devEnd.y ),
898  KiROUND( devCenter.x ), KiROUND( devCenter.y ) );
899 
900  fprintf( m_outputFile, "G01*\n" ); // Back to linear interpolate (perhaps useless here).
901 }
902 
903 
904 void GERBER_PLOTTER::PlotGerberRegion( const SHAPE_LINE_CHAIN& aPoly, void* aData )
905 {
906  if( aPoly.PointCount() <= 2 )
907  return;
908 
909  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
910 
911  bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
912 
913  if( gbr_metadata )
914  {
915  std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
916 
917  if( !attrib.empty() )
918  {
919  fputs( attrib.c_str(), m_outputFile );
920  clearTA_AperFunction = true;
921  }
922  }
923 
924  PlotPoly( aPoly, FILL_TYPE::FILLED_SHAPE, 0 , gbr_metadata );
925 
926  // Clear the TA attribute, to avoid the next item to inherit it:
927  if( clearTA_AperFunction )
928  {
929  if( m_useX2format )
930  {
931  fputs( "%TD.AperFunction*%\n", m_outputFile );
932  }
933  else
934  {
935  fputs( "G04 #@! TD.AperFunction*\n", m_outputFile );
936  }
937  }
938 }
939 
940 
941 void GERBER_PLOTTER::PlotGerberRegion( const std::vector< wxPoint >& aCornerList, void* aData )
942 {
943  if( aCornerList.size() <= 2 )
944  return;
945 
946  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
947 
948  bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
949 
950  if( gbr_metadata )
951  {
952  std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
953 
954  if( !attrib.empty() )
955  {
956  fputs( attrib.c_str(), m_outputFile );
957  clearTA_AperFunction = true;
958  }
959  }
960 
961  PlotPoly( aCornerList, FILL_TYPE::FILLED_SHAPE, 0, gbr_metadata );
962 
963  // Clear the TA attribute, to avoid the next item to inherit it:
964  if( clearTA_AperFunction )
965  {
966  if( m_useX2format )
967  {
968  fputs( "%TD.AperFunction*%\n", m_outputFile );
969  }
970  else
971  {
972  fputs( "G04 #@! TD.AperFunction*\n", m_outputFile );
973  }
974  }
975 }
976 
977 
978 void GERBER_PLOTTER::PlotPoly( const SHAPE_LINE_CHAIN& aPoly, FILL_TYPE aFill, int aWidth,
979  void* aData )
980 {
981  if( aPoly.CPoints().size() <= 1 )
982  return;
983 
984  // Gerber format does not know filled polygons with thick outline
985  // Therefore, to plot a filled polygon with outline having a thickness,
986  // one should plot outline as thick segments
987  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
988 
989  if( gbr_metadata )
990  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
991 
992  if( aFill != FILL_TYPE::NO_FILL )
993  {
994  fputs( "G36*\n", m_outputFile );
995 
996  MoveTo( wxPoint( aPoly.CPoint( 0 ) ) );
997 
998  fputs( "G01*\n", m_outputFile ); // Set linear interpolation.
999 
1000  for( int ii = 1; ii < aPoly.PointCount(); ii++ )
1001  {
1002  int arcindex = aPoly.ArcIndex( ii );
1003 
1004  if( arcindex < 0 )
1005  {
1007  LineTo( wxPoint( aPoly.CPoint( ii ) ) );
1008  }
1009  else
1010  {
1011  const SHAPE_ARC& arc = aPoly.Arc( arcindex );
1012 
1013  plotArc( arc, ii > 0 );
1014  }
1015  }
1016 
1017  // If the polygon is not closed, close it:
1018  if( aPoly.CPoint( 0 ) != aPoly.CPoint( -1 ) )
1019  FinishTo( wxPoint( aPoly.CPoint( 0 ) ) );
1020 
1021  fputs( "G37*\n", m_outputFile );
1022  }
1023 
1024  if( aWidth > 0 ) // Draw the polyline/polygon outline
1025  {
1026  SetCurrentLineWidth( aWidth, gbr_metadata );
1027 
1028  MoveTo( wxPoint( aPoly.CPoint( 0 ) ) );
1029 
1030  for( int ii = 1; ii < aPoly.PointCount(); ii++ )
1031  {
1032  int arcindex = aPoly.ArcIndex( ii );
1033 
1034  if( arcindex < 0 )
1035  {
1037  LineTo( wxPoint( aPoly.CPoint( ii ) ) );
1038  }
1039  else
1040  {
1041  const SHAPE_ARC& arc = aPoly.Arc( arcindex );
1042 
1043  plotArc( arc, ii > 0 );
1044  }
1045  }
1046 
1047  // Ensure the thick outline is closed for filled polygons
1048  // (if not filled, could be only a polyline)
1049  if( aFill != FILL_TYPE::NO_FILL && ( aPoly.CPoint( 0 ) != aPoly.CPoint( -1 ) ) )
1050  LineTo( wxPoint( aPoly.CPoint( 0 ) ) );
1051 
1052  PenFinish();
1053  }
1054 }
1055 
1056 void GERBER_PLOTTER::PlotPoly( const std::vector< wxPoint >& aCornerList,
1057  FILL_TYPE aFill, int aWidth, void * aData )
1058 {
1059  if( aCornerList.size() <= 1 )
1060  return;
1061 
1062  // Gerber format does not know filled polygons with thick outline
1063  // Therefore, to plot a filled polygon with outline having a thickness,
1064  // one should plot outline as thick segments
1065  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1066 
1067  if( gbr_metadata )
1068  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1069 
1070  if( aFill != FILL_TYPE::NO_FILL )
1071  {
1072  fputs( "G36*\n", m_outputFile );
1073 
1074  MoveTo( aCornerList[0] );
1075  fputs( "G01*\n", m_outputFile ); // Set linear interpolation.
1076 
1077  for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1078  LineTo( aCornerList[ii] );
1079 
1080  // If the polygon is not closed, close it:
1081  if( aCornerList[0] != aCornerList[aCornerList.size()-1] )
1082  FinishTo( aCornerList[0] );
1083 
1084  fputs( "G37*\n", m_outputFile );
1085  }
1086 
1087  if( aWidth > 0 ) // Draw the polyline/polygon outline
1088  {
1089  SetCurrentLineWidth( aWidth, gbr_metadata );
1090 
1091  MoveTo( aCornerList[0] );
1092 
1093  for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1094  LineTo( aCornerList[ii] );
1095 
1096  // Ensure the thick outline is closed for filled polygons
1097  // (if not filled, could be only a polyline)
1098  if( aFill != FILL_TYPE::NO_FILL &&( aCornerList[aCornerList.size() - 1] != aCornerList[0] ) )
1099  LineTo( aCornerList[0] );
1100 
1101  PenFinish();
1102  }
1103 }
1104 
1105 
1106 void GERBER_PLOTTER::ThickSegment( const wxPoint& start, const wxPoint& end, int width,
1107  OUTLINE_MODE tracemode, void* aData )
1108 {
1109  if( tracemode == FILLED )
1110  {
1111  GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1112  SetCurrentLineWidth( width, gbr_metadata );
1113 
1114  if( gbr_metadata )
1115  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1116 
1117  MoveTo( start );
1118  FinishTo( end );
1119  }
1120  else
1121  {
1123  segmentAsOval( start, end, width, tracemode );
1124  }
1125 }
1126 
1127 void GERBER_PLOTTER::ThickArc( const wxPoint& centre, double StAngle, double EndAngle,
1128  int radius, int width, OUTLINE_MODE tracemode, void* aData )
1129 {
1130  GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1131  SetCurrentLineWidth( width, gbr_metadata );
1132 
1133  if( gbr_metadata )
1134  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1135 
1136  if( tracemode == FILLED )
1137  {
1138  Arc( centre, StAngle, EndAngle, radius, FILL_TYPE::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1139  }
1140  else
1141  {
1143  Arc( centre, StAngle, EndAngle,
1144  radius - ( width - m_currentPenWidth ) / 2, FILL_TYPE::NO_FILL,
1146  Arc( centre, StAngle, EndAngle, radius + ( width - m_currentPenWidth ) / 2,
1148  }
1149 }
1150 
1151 
1152 void GERBER_PLOTTER::ThickRect( const wxPoint& p1, const wxPoint& p2, int width,
1153  OUTLINE_MODE tracemode, void* aData )
1154 {
1155  GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1156  SetCurrentLineWidth( width, gbr_metadata );
1157 
1158  if( gbr_metadata )
1159  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1160 
1161  if( tracemode == FILLED )
1162  {
1164  }
1165  else
1166  {
1168  wxPoint offsetp1( p1.x - (width - m_currentPenWidth) / 2,
1169  p1.y - (width - m_currentPenWidth) / 2 );
1170  wxPoint offsetp2( p2.x + (width - m_currentPenWidth) / 2,
1171  p2.y + (width - m_currentPenWidth) / 2 );
1172  Rect( offsetp1, offsetp2, FILL_TYPE::NO_FILL, -1 );
1173  offsetp1.x += (width - m_currentPenWidth);
1174  offsetp1.y += (width - m_currentPenWidth);
1175  offsetp2.x -= (width - m_currentPenWidth);
1176  offsetp2.y -= (width - m_currentPenWidth);
1177  Rect( offsetp1, offsetp2, FILL_TYPE::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1178  }
1179 }
1180 
1181 
1182 void GERBER_PLOTTER::ThickCircle( const wxPoint& pos, int diametre, int width,
1183  OUTLINE_MODE tracemode, void* aData )
1184 {
1185  GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1186  SetCurrentLineWidth( width, gbr_metadata );
1187 
1188  if( gbr_metadata )
1189  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1190 
1191  if( tracemode == FILLED )
1192  {
1193  Circle( pos, diametre, FILL_TYPE::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1194  }
1195  else
1196  {
1198  Circle( pos, diametre - (width - m_currentPenWidth),
1200  Circle( pos, diametre + (width - m_currentPenWidth),
1202  }
1203 }
1204 
1205 
1206 void GERBER_PLOTTER::FilledCircle( const wxPoint& pos, int diametre,
1207  OUTLINE_MODE tracemode, void* aData )
1208 {
1209  // A filled circle is a graphic item, not a pad.
1210  // So it is drawn, not flashed.
1211  GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1212 
1213  if( gbr_metadata )
1214  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1215 
1216  if( tracemode == FILLED )
1217  {
1218  // Draw a circle of diameter = diameter/2 with a line thickness = radius,
1219  // To create a filled circle
1220  SetCurrentLineWidth( diametre/2, gbr_metadata );
1221  Circle( pos, diametre/2, FILL_TYPE::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1222  }
1223  else
1224  {
1226  Circle( pos, diametre, FILL_TYPE::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1227  }
1228 }
1229 
1230 
1231 void GERBER_PLOTTER::FlashPadCircle( const wxPoint& pos, int diametre, OUTLINE_MODE trace_mode, void* aData )
1232 {
1233  wxSize size( diametre, diametre );
1234  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1235 
1236  if( trace_mode == SKETCH )
1237  {
1238  if( gbr_metadata )
1239  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1240 
1242 
1244  }
1245  else
1246  {
1247  DPOINT pos_dev = userToDeviceCoordinates( pos );
1248 
1249  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1250  selectAperture( size, 0, 0.0, APERTURE::AT_CIRCLE, aperture_attrib );
1251 
1252  if( gbr_metadata )
1253  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1254 
1255  emitDcode( pos_dev, 3 );
1256  }
1257 }
1258 
1259 
1260 void GERBER_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double orient,
1261  OUTLINE_MODE trace_mode, void* aData )
1262 {
1263  wxASSERT( m_outputFile );
1264  wxSize size( aSize );
1265  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1266 
1267  // Flash a vertical or horizontal shape (this is a basic aperture).
1268  if( ( orient == 0 || orient == 900 || orient == 1800 || orient == 2700 )
1269  && trace_mode == FILLED )
1270  {
1271  if( orient == 900 || orient == 2700 ) /* orientation turned 90 deg. */
1272  std::swap( size.x, size.y );
1273 
1274  DPOINT pos_dev = userToDeviceCoordinates( pos );
1275  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1276  selectAperture( size, 0, 0.0, APERTURE::AT_OVAL, aperture_attrib );
1277 
1278  if( gbr_metadata )
1279  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1280 
1281  emitDcode( pos_dev, 3 );
1282  }
1283  else // Plot pad as region.
1284  // Only regions and flashed items accept a object attribute TO.P for the pin name
1285  {
1286  if( trace_mode == FILLED )
1287  {
1288  #ifdef GBR_USE_MACROS_FOR_ROTATED_OVAL
1290  #endif
1291  {
1292  m_hasApertureRotOval = true;
1293  // We are using a aperture macro that expect size.y < size.x
1294  // i.e draw a horizontal line for rotation = 0.0
1295  // size.x = length, size.y = width
1296  if( size.x < size.y )
1297  {
1298  std::swap( size.x, size.y );
1299  orient += 900;
1300 
1301  if( orient > 1800 )
1302  orient -= 1800;
1303  }
1304 
1305  DPOINT pos_dev = userToDeviceCoordinates( pos );
1306  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1307  selectAperture( size, 0, orient/10.0, APERTURE::AM_ROTATED_OVAL, aperture_attrib );
1308 
1309  if( gbr_metadata )
1310  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1311 
1312  emitDcode( pos_dev, 3 );
1313  return;
1314  }
1315  // Draw the oval as round rect pad with a radius = 50% min size)
1316  // In gerber file, it will be drawn as a region with arcs, and can be
1317  // detected as pads (similar to a flashed pad)
1318  FlashPadRoundRect( pos, aSize, std::min( aSize.x, aSize.y ) / 2,
1319  orient, FILLED, aData );
1320  }
1321  else // Non filled shape: plot outlines:
1322  {
1323  if( size.x > size.y )
1324  {
1325  std::swap( size.x, size.y );
1326 
1327  if( orient < 2700 )
1328  orient += 900;
1329  else
1330  orient -= 2700;
1331  }
1332 
1333  sketchOval( pos, size, orient, -1 );
1334  }
1335  }
1336 }
1337 
1338 
1339 void GERBER_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& aSize,
1340  double orient, OUTLINE_MODE trace_mode, void* aData )
1341 
1342 {
1343  wxASSERT( m_outputFile );
1344  wxSize size( aSize );
1345  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1346 
1347  // Plot as an aperture flash
1348  switch( int( orient ) )
1349  {
1350  case 900:
1351  case 2700: // rotation of 90 degrees or 270 swaps sizes
1352  std::swap( size.x, size.y );
1354 
1355  case 0:
1356  case 1800:
1357  if( trace_mode == SKETCH )
1358  {
1359  if( gbr_metadata )
1360  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1361 
1363  Rect( wxPoint( pos.x - (size.x - GetCurrentLineWidth()) / 2,
1364  pos.y - (size.y - GetCurrentLineWidth()) / 2 ),
1365  wxPoint( pos.x + (size.x - GetCurrentLineWidth()) / 2,
1366  pos.y + (size.y - GetCurrentLineWidth()) / 2 ),
1368  }
1369  else
1370  {
1371  DPOINT pos_dev = userToDeviceCoordinates( pos );
1372  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1373  selectAperture( size, 0, 0.0, APERTURE::AT_RECT, aperture_attrib );
1374 
1375  if( gbr_metadata )
1376  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1377 
1378  emitDcode( pos_dev, 3 );
1379  }
1380  break;
1381 
1382  default:
1383  #ifdef GBR_USE_MACROS_FOR_ROTATED_RECT
1384  if( trace_mode != SKETCH && !m_gerberDisableApertMacros )
1385  {
1386  m_hasApertureRotRect = true;
1387  DPOINT pos_dev = userToDeviceCoordinates( pos );
1388  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1389  selectAperture( size, 0, orient/10.0, APERTURE::AM_ROT_RECT, aperture_attrib );
1390 
1391  if( gbr_metadata )
1392  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1393 
1394  emitDcode( pos_dev, 3 );
1395 
1396  break;
1397  }
1398  #endif
1399  {
1400  // plot pad shape as Gerber region
1401  wxPoint coord[4];
1402  // coord[0] is assumed the lower left
1403  // coord[1] is assumed the upper left
1404  // coord[2] is assumed the upper right
1405  // coord[3] is assumed the lower right
1406 
1407  coord[0].x = -size.x/2; // lower left
1408  coord[0].y = size.y/2;
1409  coord[1].x = -size.x/2; // upper left
1410  coord[1].y = -size.y/2;
1411  coord[2].x = size.x/2; // upper right
1412  coord[2].y = -size.y/2;
1413  coord[3].x = size.x/2; // lower right
1414  coord[3].y = size.y/2;
1415 
1416  FlashPadTrapez( pos, coord, orient, trace_mode, aData );
1417  }
1418  break;
1419  }
1420 }
1421 
1422 void GERBER_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSize,
1423  int aCornerRadius, double aOrient,
1424  OUTLINE_MODE aTraceMode, void* aData )
1425 
1426 {
1427  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1428 
1429  if( aTraceMode != FILLED )
1430  {
1431  SHAPE_POLY_SET outline;
1432  TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient, aCornerRadius,
1433  0.0, 0, 0, GetPlotterArcHighDef(), ERROR_INSIDE );
1434 
1435  SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata );
1436  outline.Inflate( -GetCurrentLineWidth()/2, 16 );
1437 
1438  std::vector< wxPoint > cornerList;
1439  // TransformRoundRectToPolygon creates only one convex polygon
1440  SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
1441  cornerList.reserve( poly.PointCount() + 1 );
1442 
1443  for( int ii = 0; ii < poly.PointCount(); ++ii )
1444  cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
1445 
1446  // Close polygon
1447  cornerList.push_back( cornerList[0] );
1448 
1449  // plot outlines
1450  PlotPoly( cornerList, FILL_TYPE::NO_FILL, GetCurrentLineWidth(), gbr_metadata );
1451  }
1452  else
1453  {
1454  #ifdef GBR_USE_MACROS_FOR_ROUNDRECT
1456  #endif
1457  {
1458  m_hasApertureRoundRect = true;
1459 
1460  DPOINT pos_dev = userToDeviceCoordinates( aPadPos );
1461  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1462  selectAperture( aSize, aCornerRadius, aOrient/10.0,
1463  APERTURE::AM_ROUND_RECT, aperture_attrib );
1464 
1465  if( gbr_metadata )
1466  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1467 
1468  emitDcode( pos_dev, 3 );
1469  return;
1470  }
1471 
1472  // A Pad RoundRect is plotted as a Gerber region.
1473  // Initialize region metadata:
1474  bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
1475 
1476  if( gbr_metadata )
1477  {
1478  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1479  std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
1480 
1481  if( !attrib.empty() )
1482  {
1483  fputs( attrib.c_str(), m_outputFile );
1484  clearTA_AperFunction = true;
1485  }
1486  }
1487 
1488  // Plot the region using arcs in corners.
1489  plotRoundRectAsRegion( aPadPos, aSize, aCornerRadius, aOrient );
1490 
1491  // Clear the TA attribute, to avoid the next item to inherit it:
1492  if( clearTA_AperFunction )
1493  {
1494  if( m_useX2format )
1495  fputs( "%TD.AperFunction*%\n", m_outputFile );
1496  else
1497  fputs( "G04 #@! TD.AperFunction*\n", m_outputFile );
1498  }
1499  }
1500 }
1501 
1502 
1503 void GERBER_PLOTTER::plotRoundRectAsRegion( const wxPoint& aRectCenter, const wxSize& aSize,
1504  int aCornerRadius, double aOrient )
1505 {
1506  // The region outline is generated by 4 sides and 4 90 deg arcs
1507  // 1 --- 2
1508  // | c |
1509  // 4 --- 3
1510 
1511  // Note also in user coordinates the Y axis is from top to bottom
1512  // for historical reasons.
1513 
1514  // A helper structure to handle outlines coordinates (segments and arcs)
1515  // in user coordinates
1516  struct RR_EDGE
1517  {
1518  wxPoint m_start;
1519  wxPoint m_end;
1520  wxPoint m_center;
1521  // in decidegrees: angle start. angle end = m_arc_angle_start+arc_angle
1522  double m_arc_angle_start;
1523  };
1524 
1525  const double arc_angle = -900.0; // in decidegrees
1526  int hsizeX = aSize.x/2;
1527  int hsizeY = aSize.y/2;
1528 
1529  RR_EDGE curr_edge;
1530  std::vector<RR_EDGE> rr_outline;
1531 
1532  // Build outline coordinates, relative to rectangle center, rotation 0:
1533 
1534  // Top left corner 1 (and 4 to 1 left vertical side @ x=-hsizeX)
1535  curr_edge.m_start.x = -hsizeX;
1536  curr_edge.m_start.y = hsizeY - aCornerRadius;
1537  curr_edge.m_end.x = curr_edge.m_start.x;
1538  curr_edge.m_end.y = -hsizeY + aCornerRadius;
1539  curr_edge.m_center.x = -hsizeX + aCornerRadius;
1540  curr_edge.m_center.y = curr_edge.m_end.y;
1541  curr_edge.m_arc_angle_start = aOrient + 1800.0; // En decidegree
1542 
1543  rr_outline.push_back( curr_edge );
1544 
1545  // Top right corner 2 (and 1 to 2 top horizontal side @ y=-hsizeY)
1546  curr_edge.m_start.x = -hsizeX + aCornerRadius;
1547  curr_edge.m_start.y = -hsizeY;
1548  curr_edge.m_end.x = hsizeX - aCornerRadius;
1549  curr_edge.m_end.y = curr_edge.m_start.y;
1550  curr_edge.m_center.x = curr_edge.m_end.x;
1551  curr_edge.m_center.y = -hsizeY + aCornerRadius;
1552  curr_edge.m_arc_angle_start = aOrient + 900.0; // En decidegree
1553 
1554  rr_outline.push_back( curr_edge );
1555 
1556  // bottom right corner 3 (and 2 to 3 right vertical side @ x=hsizeX)
1557  curr_edge.m_start.x = hsizeX;
1558  curr_edge.m_start.y = -hsizeY + aCornerRadius;
1559  curr_edge.m_end.x = curr_edge.m_start.x;
1560  curr_edge.m_end.y = hsizeY - aCornerRadius;
1561  curr_edge.m_center.x = hsizeX - aCornerRadius;
1562  curr_edge.m_center.y = curr_edge.m_end.y;
1563  curr_edge.m_arc_angle_start = aOrient + 0.0; // En decidegree
1564 
1565  rr_outline.push_back( curr_edge );
1566 
1567  // bottom left corner 4 (and 3 to 4 bottom horizontal side @ y=hsizeY)
1568  curr_edge.m_start.x = hsizeX - aCornerRadius;
1569  curr_edge.m_start.y = hsizeY;
1570  curr_edge.m_end.x = -hsizeX + aCornerRadius;
1571  curr_edge.m_end.y = curr_edge.m_start.y;
1572  curr_edge.m_center.x = curr_edge.m_end.x;
1573  curr_edge.m_center.y = hsizeY - aCornerRadius;
1574  curr_edge.m_arc_angle_start = aOrient - 900.0; // En decidegree
1575 
1576  rr_outline.push_back( curr_edge );
1577 
1578  // Move relative coordinates to the actual location and rotation:
1579  wxPoint arc_last_center;
1580  int arc_last_angle = curr_edge.m_arc_angle_start+arc_angle;
1581 
1582  for( RR_EDGE& rr_edge: rr_outline )
1583  {
1584  RotatePoint( &rr_edge.m_start, aOrient );
1585  RotatePoint( &rr_edge.m_end, aOrient );
1586  RotatePoint( &rr_edge.m_center, aOrient );
1587  rr_edge.m_start += aRectCenter;
1588  rr_edge.m_end += aRectCenter;
1589  rr_edge.m_center += aRectCenter;
1590  arc_last_center = rr_edge.m_center;
1591  }
1592 
1593  // Ensure the region is a closed polygon, i.e. the end point of last segment
1594  // (end of arc) is the same as the first point. Rounding issues can create a
1595  // small difference, mainly for rotated pads.
1596  // calculate last point (end of last arc):
1597  wxPoint last_pt;
1598  last_pt.x = arc_last_center.x + KiROUND( cosdecideg( aCornerRadius, arc_last_angle ) );
1599  last_pt.y = arc_last_center.y - KiROUND( sindecideg( aCornerRadius, arc_last_angle ) );
1600 
1601  wxPoint first_pt = rr_outline[0].m_start;
1602 
1603 #if 0 // For test only:
1604  if( last_pt != first_pt )
1605  wxLogMessage( "first pt %d %d last pt %d %d",
1606  first_pt.x, first_pt.y, last_pt.x, last_pt.y );
1607 #endif
1608 
1609  fputs( "G36*\n", m_outputFile ); // Start region
1610  fputs( "G01*\n", m_outputFile ); // Set linear interpolation.
1611  first_pt = last_pt;
1612  MoveTo( first_pt ); // Start point of region, must be same as end point
1613 
1614  for( RR_EDGE& rr_edge: rr_outline )
1615  {
1616  if( aCornerRadius ) // Guard: ensure we do not create arcs with radius = 0
1617  {
1618  // LineTo( rr_edge.m_end ); // made in plotArc()
1619  plotArc( rr_edge.m_center, rr_edge.m_arc_angle_start,
1620  rr_edge.m_arc_angle_start+arc_angle, aCornerRadius, true );
1621  }
1622  else
1623  {
1624  LineTo( rr_edge.m_end );
1625  }
1626  }
1627 
1628  fputs( "G37*\n", m_outputFile ); // Close region
1629 }
1630 
1631 
1632 void GERBER_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize,
1633  double aOrient, SHAPE_POLY_SET* aPolygons,
1634  OUTLINE_MODE aTraceMode, void* aData )
1635 
1636 {
1637  // A Pad custom is plotted as polygon (a region in Gerber language).
1638  GBR_METADATA gbr_metadata;
1639 
1640  if( aData )
1641  gbr_metadata = *static_cast<GBR_METADATA*>( aData );
1642 
1643  SHAPE_POLY_SET polyshape = *aPolygons;
1644 
1645  if( aTraceMode != FILLED )
1646  {
1647  SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata );
1648  polyshape.Inflate( -GetCurrentLineWidth()/2, 16 );
1649  }
1650 
1651  std::vector< wxPoint > cornerList;
1652 
1653  for( int cnt = 0; cnt < polyshape.OutlineCount(); ++cnt )
1654  {
1655  SHAPE_LINE_CHAIN& poly = polyshape.Outline( cnt );
1656 
1657  cornerList.clear();
1658 
1659  for( int ii = 0; ii < poly.PointCount(); ++ii )
1660  cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
1661 
1662  // Close polygon
1663  cornerList.push_back( cornerList[0] );
1664 
1665  if( aTraceMode == SKETCH )
1666  {
1667  PlotPoly( cornerList, FILL_TYPE::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
1668  }
1669  else
1670  {
1671 #ifdef GBR_USE_MACROS_FOR_CUSTOM_PAD
1673  || cornerList.size() > GBR_MACRO_FOR_CUSTOM_PAD_MAX_CORNER_COUNT )
1674  PlotGerberRegion( cornerList, &gbr_metadata );
1675  else
1676  {
1677  // An AM will be created. the shape must be in position 0,0 and orientation 0
1678  // to be able to reuse the same AM for pads having the same shape
1679  for( size_t ii = 0; ii < cornerList.size(); ii++ )
1680  {
1681  cornerList[ii] -= aPadPos;
1682  RotatePoint( &cornerList[ii], -aOrient );
1683  }
1684 
1685  DPOINT pos_dev = userToDeviceCoordinates( aPadPos );
1686  selectAperture( cornerList, aOrient/10.0,
1687  APERTURE::AM_FREE_POLYGON, gbr_metadata.GetApertureAttrib() );
1688  formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1689 
1690  emitDcode( pos_dev, 3 );
1691  }
1692 #else
1693  PlotGerberRegion( cornerList, &gbr_metadata );
1694 #endif
1695  }
1696  }
1697 }
1698 
1699 
1700 void GERBER_PLOTTER::FlashPadChamferRoundRect( const wxPoint& aShapePos, const wxSize& aPadSize,
1701  int aCornerRadius, double aChamferRatio,
1702  int aChamferPositions, double aPadOrient,
1703  OUTLINE_MODE aPlotMode, void* aData )
1704 
1705 {
1706  GBR_METADATA gbr_metadata;
1707 
1708  if( aData )
1709  gbr_metadata = *static_cast<GBR_METADATA*>( aData );
1710 
1711  DPOINT pos_dev = userToDeviceCoordinates( aShapePos );
1712 
1713  SHAPE_POLY_SET outline;
1714  // polygon corners list
1715  std::vector<wxPoint> cornerList;
1716 
1717  bool hasRoundedCorner = aCornerRadius != 0 && aChamferPositions != 15;
1718 
1719 #ifdef GBR_USE_MACROS_FOR_CHAMFERED_RECT
1720  // Sketch mode or round rect shape or Apert Macros disabled
1721  if( aPlotMode != FILLED || hasRoundedCorner || m_gerberDisableApertMacros )
1722 #endif
1723  {
1724  TransformRoundChamferedRectToPolygon( outline, aShapePos, aPadSize, aPadOrient,
1725  aCornerRadius, aChamferRatio, aChamferPositions, 0,
1727 
1728  // Build the corner list
1729  const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
1730 
1731  for( int ii = 0; ii < corners.PointCount(); ii++ )
1732  cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
1733 
1734  // Close the polygon
1735  cornerList.push_back( cornerList[0] );
1736 
1737  if( aPlotMode == SKETCH )
1738  PlotPoly( cornerList, FILL_TYPE::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
1739  else
1740  {
1741 #ifdef GBR_USE_MACROS_FOR_CHAMFERED_ROUND_RECT
1743  {
1744  PlotGerberRegion( cornerList, &gbr_metadata );
1745  }
1746  else
1747  {
1748  // An AM will be created. the shape must be in position 0,0 and orientation 0
1749  // to be able to reuse the same AM for pads having the same shape
1750  for( size_t ii = 0; ii < cornerList.size(); ii++ )
1751  {
1752  cornerList[ii] -= aShapePos;
1753  RotatePoint( &cornerList[ii], -aPadOrient );
1754  }
1755 
1756  selectAperture( cornerList, aPadOrient/10.0,
1757  APERTURE::AM_FREE_POLYGON, gbr_metadata.GetApertureAttrib() );
1758  formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1759 
1760  emitDcode( pos_dev, 3 );
1761  }
1762 #else
1763  PlotGerberRegion( cornerList, &gbr_metadata );
1764 #endif
1765  }
1766 
1767  return;
1768  }
1769 
1770  // Build the chamfered polygon (4 to 8 corners )
1771  TransformRoundChamferedRectToPolygon( outline, wxPoint( 0, 0 ), aPadSize, 0.0, 0,
1772  aChamferRatio, aChamferPositions, 0,
1774 
1775  // Build the corner list
1776  const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
1777 
1778  // Generate the polygon (4 to 8 corners )
1779  for( int ii = 0; ii < corners.PointCount(); ii++ )
1780  cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
1781 
1782  switch( cornerList.size() )
1783  {
1784  case 4:
1785  m_hasApertureOutline4P = true;
1786  selectAperture( cornerList, aPadOrient/10.0,
1788  break;
1789 
1790  case 5:
1792  selectAperture( cornerList, aPadOrient/10.0,
1794  break;
1795 
1796  case 6:
1798  selectAperture( cornerList, aPadOrient/10.0,
1800  break;
1801 
1802  case 7:
1804  selectAperture( cornerList, aPadOrient/10.0,
1806  break;
1807 
1808  case 8:
1810  selectAperture( cornerList, aPadOrient/10.0,
1812  break;
1813 
1814  default:
1815  wxLogMessage( "FlashPadChamferRoundRect(): Unexpected number of corners (%d)",
1816  (int)cornerList.size() );
1817  break;
1818  }
1819 
1820  formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1821 
1822  emitDcode( pos_dev, 3 );
1823 }
1824 
1825 
1826 void GERBER_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCorners,
1827  double aPadOrient, OUTLINE_MODE aTrace_Mode, void* aData )
1828 
1829 {
1830  // polygon corners list
1831  std::vector<wxPoint> cornerList = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
1832 
1833  // Draw the polygon and fill the interior as required
1834  for( unsigned ii = 0; ii < 4; ii++ )
1835  {
1836  RotatePoint( &cornerList[ii], aPadOrient );
1837  cornerList[ii] += aPadPos;
1838  }
1839 
1840  // Close the polygon
1841  cornerList.push_back( cornerList[0] );
1842  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1843 
1844  GBR_METADATA metadata;
1845 
1846  if( gbr_metadata )
1847  metadata = *gbr_metadata;
1848 
1849  if( aTrace_Mode == SKETCH )
1850  {
1851  PlotPoly( cornerList, FILL_TYPE::NO_FILL, GetCurrentLineWidth(), &metadata );
1852  return;
1853  }
1854 
1855  // Plot a filled polygon:
1856  #ifdef GBR_USE_MACROS_FOR_TRAPEZOID
1858  #endif
1859  {
1860  m_hasApertureOutline4P = true;
1861  DPOINT pos_dev = userToDeviceCoordinates( aPadPos );
1862  // polygon corners list
1863  std::vector<wxPoint> corners = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
1864  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1865  selectAperture( corners, aPadOrient/10.0, APERTURE::APER_MACRO_OUTLINE4P, aperture_attrib );
1866 
1867  if( gbr_metadata )
1868  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1869 
1870  emitDcode( pos_dev, 3 );
1871  return;
1872  }
1873 
1874  PlotGerberRegion( cornerList, &metadata );
1875 }
1876 
1877 
1878 void GERBER_PLOTTER::FlashRegularPolygon( const wxPoint& aShapePos, int aDiameter,
1879  int aCornerCount, double aOrient,
1880  OUTLINE_MODE aTraceMode, void* aData )
1881 {
1882  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1883 
1884  GBR_METADATA metadata;
1885 
1886  if( gbr_metadata )
1887  metadata = *gbr_metadata;
1888 
1889  if( aTraceMode == SKETCH )
1890  {
1891  // Build the polygon:
1892  std::vector< wxPoint > cornerList;
1893 
1894  double angle_delta = 3600.0 / aCornerCount; // in 0.1 degree
1895 
1896  for( int ii = 0; ii < aCornerCount; ii++ )
1897  {
1898  double rot = aOrient + (angle_delta*ii);
1899  wxPoint vertice( aDiameter/2, 0 );
1900  RotatePoint( &vertice, rot );
1901  vertice += aShapePos;
1902  cornerList.push_back( vertice );
1903  }
1904 
1905  cornerList.push_back( cornerList[0] ); // Close the shape
1906 
1907  PlotPoly( cornerList, FILL_TYPE::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
1908  }
1909  else
1910  {
1911  DPOINT pos_dev = userToDeviceCoordinates( aShapePos );
1912 
1913  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1914 
1915  APERTURE::APERTURE_TYPE apert_type =
1917  selectAperture( aDiameter, aOrient, apert_type, aperture_attrib );
1918 
1919  if( gbr_metadata )
1920  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1921 
1922  emitDcode( pos_dev, 3 );
1923  }
1924 }
1925 
1926 
1927 void GERBER_PLOTTER::Text( const wxPoint& aPos, const COLOR4D& aColor,
1928  const wxString& aText, double aOrient, const wxSize& aSize,
1929  enum EDA_TEXT_HJUSTIFY_T aH_justify,
1930  enum EDA_TEXT_VJUSTIFY_T aV_justify, int aWidth, bool aItalic,
1931  bool aBold, bool aMultilineAllowed, void* aData )
1932 {
1933  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1934 
1935  if( gbr_metadata )
1936  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1937 
1938  PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth, aItalic,
1939  aBold, aMultilineAllowed, aData );
1940 }
1941 
1942 
1943 void GERBER_PLOTTER::SetLayerPolarity( bool aPositive )
1944 {
1945  if( aPositive )
1946  fprintf( m_outputFile, "%%LPD*%%\n" );
1947  else
1948  fprintf( m_outputFile, "%%LPC*%%\n" );
1949 }
1950 
1951 
1952 bool APER_MACRO_FREEPOLY::IsSamePoly( const std::vector<wxPoint>& aPolygon ) const
1953 {
1954  return polyCompare( m_Corners, aPolygon );
1955 }
1956 
1957 
1958 void APER_MACRO_FREEPOLY::Format( FILE * aOutput, double aIu2GbrMacroUnit )
1959 {
1960  // Write aperture header
1961  fprintf( aOutput, "%%AM%s%d*\n", AM_FREEPOLY_BASENAME, m_Id );
1962  fprintf( aOutput, "4,1,%d,", (int)m_Corners.size() );
1963 
1964  // Insert a newline after curr_line_count_max coordinates.
1965  int curr_line_corner_count = 0;
1966  const int curr_line_count_max = 20; // <= 0 to disable newlines
1967 
1968  for( size_t ii = 0; ii <= m_Corners.size(); ii++ )
1969  {
1970  int jj = ii;
1971 
1972  if( ii >= m_Corners.size() )
1973  jj = 0;
1974 
1975  // Note: parameter values are always mm or inches
1976  fprintf( aOutput, "%#f,%#f,",
1977  m_Corners[jj].x * aIu2GbrMacroUnit, -m_Corners[jj].y * aIu2GbrMacroUnit );
1978 
1979  if( curr_line_count_max >= 0 && ++curr_line_corner_count >= curr_line_count_max )
1980  {
1981  fprintf( aOutput, "\n" );
1982  curr_line_corner_count = 0;
1983  }
1984  }
1985 
1986  // output rotation parameter
1987  fputs( "$1*%\n", aOutput );
1988 }
1989 
1990 
1991 void APER_MACRO_FREEPOLY_LIST::Format( FILE * aOutput, double aIu2GbrMacroUnit )
1992 {
1993  for( int idx = 0; idx < AmCount(); idx++ )
1994  m_AMList[idx].Format( aOutput, aIu2GbrMacroUnit );
1995 }
1996 
1997 
1998 void APER_MACRO_FREEPOLY_LIST::Append( const std::vector<wxPoint>& aPolygon )
1999 {
2000  m_AMList.emplace_back( aPolygon, AmCount() );
2001 }
2002 
2003 
2004 int APER_MACRO_FREEPOLY_LIST::FindAm( const std::vector<wxPoint>& aPolygon ) const
2005 {
2006  for( int idx = 0; idx < AmCount(); idx++ )
2007  {
2008  if( m_AMList[idx].IsSamePoly( aPolygon ) )
2009  return idx;
2010  }
2011 
2012  return -1;
2013 }
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:453
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:273
Handle special data (items attributes) during plot.
#define APER_MACRO_ROUNDRECT_NAME
#define APER_MACRO_ROT_RECT_HEADER
specialized plotter for GERBER files format
const SHAPE_ARC & Arc(size_t aArc) const
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:279
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.
virtual void EndBlock(void *aData) override
Define the end of a group of drawing items the group is started by StartBlock().
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:590
double m_iuPerDeviceUnit
Definition: plotter.h:581
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
double GetStartAngle() const
Definition: shape_arc.cpp:405
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:229
double m_IUsPerDecimil
Definition: plotter.h:579
int PointCount() const
Return the number of points (vertices) in this line chain.
wxArrayString m_headerExtraLines
Definition: plotter.h:605
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
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.
void TransformRoundChamferedRectToPolygon(SHAPE_POLY_SET &aCornerBuffer, const wxPoint &aPosition, const wxSize &aSize, double 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.
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.
bool m_hasApertureRoundRect
double m_plotScale
Plot scale - chosen by the user (even implicitly with 'fit in a4')
Definition: plotter.h:573
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
virtual int GetCurrentLineWidth() const
Definition: plotter.h:168
#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
Return a reference to a given point in the line chain.
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:268
static const int USE_DEFAULT_LINE_WIDTH
Definition: plotter.h:126
std::vector< APERTURE > m_apertures
bool m_plotMirror
Definition: plotter.h:584
int m_currentPenWidth
Definition: plotter.h:595
#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.
const std::vector< VECTOR2I > & CPoints() const
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
const VECTOR2I & GetP0() const
Definition: shape_arc.h:111
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
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=nullptr) override
Draw text with the plotter.
RENDER_SETTINGS * m_renderSettings
Definition: plotter.h:607
virtual void SetCurrentLineWidth(int width, void *aData=nullptr) override
Set the line width for the next drawing.
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.
std::vector< APER_MACRO_FREEPOLY > m_AMList
#define AM_FREEPOLY_BASENAME
double GetEndAngle() const
Definition: shape_arc.cpp:415
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.
void Inflate(int aAmount, int aCircleSegCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
Perform outline inflation/deflation.
EDA_TEXT_VJUSTIFY_T
Definition: eda_text.h:68
Plotting engine (Gerber)
void MoveTo(const wxPoint &pos)
Definition: plotter.h:263
virtual DPOINT userToDeviceCoordinates(const wxPoint &aCoordinate)
Modify 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:452
#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:443
int GetWidth() const
Definition: shape_arc.h:130
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:583
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
virtual bool EndPlot() override
Represent a polyline (an zero-thickness chain of connected line segments).
#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:73
int GetPlotterArcHighDef() const
Definition: plotter.h:228
char m_penState
Definition: plotter.h:596
virtual void SetGerberCoordinatesFormat(int aResolution, bool aUseInches=false) override
Selection of Gerber units and resolution (number of digits in mantissa).
int GetDefaultPenWidth() const
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=nullptr)
Draw text with the plotter.
Definition: gr_text.cpp:220
#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 PlotGerberRegion(const std::vector< wxPoint > &aCornerList, void *aData=nullptr)
Plot a Gerber region: similar to PlotPoly but plot only filled polygon, and add the TA....
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:603
void sketchOval(const wxPoint &pos, const wxSize &size, double orient, int width)
Definition: plotter.cpp:474
const VECTOR2I & GetP1() const
Definition: shape_arc.h:112
VECTOR2I GetCenter() const
Definition: shape_arc.cpp:425
wxString m_creator
Definition: plotter.h:599
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:103