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