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 <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( "" );
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, "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  double start_angle = aArc.GetStartAngle();
846  double end_angle = aArc.GetEndAngle();
847 
848  if( !aPlotInRegion )
849  MoveTo( start);
850  else
851  LineTo( start );
852 
853  DPOINT devEnd = userToDeviceCoordinates( end );
854  DPOINT devCenter = userToDeviceCoordinates( center ) - userToDeviceCoordinates( start );
855 
856  fprintf( m_outputFile, "G75*\n" ); // Multiquadrant (360 degrees) mode
857 
858  if( start_angle < end_angle )
859  fprintf( m_outputFile, "G03*\n" ); // Active circular interpolation, CCW
860  else
861  fprintf( m_outputFile, "G02*\n" ); // Active circular interpolation, CW
862 
863  fprintf( m_outputFile, "X%dY%dI%dJ%dD01*\n",
864  KiROUND( devEnd.x ), KiROUND( devEnd.y ),
865  KiROUND( devCenter.x ), KiROUND( devCenter.y ) );
866 
867  fprintf( m_outputFile, "G01*\n" ); // Back to linear interpolate (perhaps useless here).
868 }
869 
870 
871 void GERBER_PLOTTER::plotArc( const wxPoint& aCenter, double aStAngle, double aEndAngle,
872  int aRadius, bool aPlotInRegion )
873 {
874  wxPoint start, end;
875  start.x = aCenter.x + KiROUND( cosdecideg( aRadius, aStAngle ) );
876  start.y = aCenter.y - KiROUND( sindecideg( aRadius, aStAngle ) );
877 
878  if( !aPlotInRegion )
879  MoveTo( start );
880  else
881  LineTo( start );
882 
883  end.x = aCenter.x + KiROUND( cosdecideg( aRadius, aEndAngle ) );
884  end.y = aCenter.y - KiROUND( sindecideg( aRadius, aEndAngle ) );
885  DPOINT devEnd = userToDeviceCoordinates( end );
886  DPOINT devCenter = userToDeviceCoordinates( aCenter ) - userToDeviceCoordinates( start );
887 
888  fprintf( m_outputFile, "G75*\n" ); // Multiquadrant (360 degrees) mode
889 
890  if( aStAngle < aEndAngle )
891  fprintf( m_outputFile, "G03*\n" ); // Active circular interpolation, CCW
892  else
893  fprintf( m_outputFile, "G02*\n" ); // Active circular interpolation, CW
894 
895  fprintf( m_outputFile, "X%dY%dI%dJ%dD01*\n",
896  KiROUND( devEnd.x ), KiROUND( devEnd.y ),
897  KiROUND( devCenter.x ), KiROUND( devCenter.y ) );
898 
899  fprintf( m_outputFile, "G01*\n" ); // Back to linear interpolate (perhaps useless here).
900 }
901 
902 
903 void GERBER_PLOTTER::PlotGerberRegion( const SHAPE_LINE_CHAIN& aPoly, void* aData )
904 {
905  if( aPoly.PointCount() <= 2 )
906  return;
907 
908  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
909 
910  bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
911 
912  if( gbr_metadata )
913  {
914  std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
915 
916  if( !attrib.empty() )
917  {
918  fputs( attrib.c_str(), m_outputFile );
919  clearTA_AperFunction = true;
920  }
921  }
922 
923  PlotPoly( aPoly, FILL_T::FILLED_SHAPE, 0 , gbr_metadata );
924 
925  // Clear the TA attribute, to avoid the next item to inherit it:
926  if( clearTA_AperFunction )
927  {
928  if( m_useX2format )
929  {
930  fputs( "%TD.AperFunction*%\n", m_outputFile );
931  }
932  else
933  {
934  fputs( "G04 #@! TD.AperFunction*\n", m_outputFile );
935  }
936  }
937 }
938 
939 
940 void GERBER_PLOTTER::PlotGerberRegion( const std::vector< wxPoint >& aCornerList, void* aData )
941 {
942  if( aCornerList.size() <= 2 )
943  return;
944 
945  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
946 
947  bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
948 
949  if( gbr_metadata )
950  {
951  std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
952 
953  if( !attrib.empty() )
954  {
955  fputs( attrib.c_str(), m_outputFile );
956  clearTA_AperFunction = true;
957  }
958  }
959 
960  PlotPoly( aCornerList, FILL_T::FILLED_SHAPE, 0, gbr_metadata );
961 
962  // Clear the TA attribute, to avoid the next item to inherit it:
963  if( clearTA_AperFunction )
964  {
965  if( m_useX2format )
966  {
967  fputs( "%TD.AperFunction*%\n", m_outputFile );
968  }
969  else
970  {
971  fputs( "G04 #@! TD.AperFunction*\n", m_outputFile );
972  }
973  }
974 }
975 
976 
977 void GERBER_PLOTTER::PlotPoly( const SHAPE_LINE_CHAIN& aPoly, FILL_T aFill, int aWidth,
978  void* aData )
979 {
980  if( aPoly.CPoints().size() <= 1 )
981  return;
982 
983  // Gerber format does not know filled polygons with thick outline
984  // Therefore, to plot a filled polygon with outline having a thickness,
985  // one should plot outline as thick segments
986  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
987 
988  if( gbr_metadata )
989  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
990 
991  if( aFill != FILL_T::NO_FILL )
992  {
993  fputs( "G36*\n", m_outputFile );
994 
995  MoveTo( wxPoint( aPoly.CPoint( 0 ) ) );
996 
997  fputs( "G01*\n", m_outputFile ); // Set linear interpolation.
998 
999  for( int ii = 1; ii < aPoly.PointCount(); ii++ )
1000  {
1001  int arcindex = aPoly.ArcIndex( ii );
1002 
1003  if( arcindex < 0 )
1004  {
1006  LineTo( wxPoint( aPoly.CPoint( ii ) ) );
1007  }
1008  else
1009  {
1010  const SHAPE_ARC& arc = aPoly.Arc( arcindex );
1011 
1012  plotArc( arc, ii > 0 );
1013  }
1014  }
1015 
1016  // If the polygon is not closed, close it:
1017  if( aPoly.CPoint( 0 ) != aPoly.CPoint( -1 ) )
1018  FinishTo( wxPoint( aPoly.CPoint( 0 ) ) );
1019 
1020  fputs( "G37*\n", m_outputFile );
1021  }
1022 
1023  if( aWidth > 0 ) // Draw the polyline/polygon outline
1024  {
1025  SetCurrentLineWidth( aWidth, gbr_metadata );
1026 
1027  MoveTo( wxPoint( aPoly.CPoint( 0 ) ) );
1028 
1029  for( int ii = 1; ii < aPoly.PointCount(); ii++ )
1030  {
1031  int arcindex = aPoly.ArcIndex( ii );
1032 
1033  if( arcindex < 0 )
1034  {
1036  LineTo( wxPoint( aPoly.CPoint( ii ) ) );
1037  }
1038  else
1039  {
1040  const SHAPE_ARC& arc = aPoly.Arc( arcindex );
1041 
1042  plotArc( arc, ii > 0 );
1043  }
1044  }
1045 
1046  // Ensure the thick outline is closed for filled polygons
1047  // (if not filled, could be only a polyline)
1048  if( aFill != FILL_T::NO_FILL && ( aPoly.CPoint( 0 ) != aPoly.CPoint( -1 ) ) )
1049  LineTo( wxPoint( aPoly.CPoint( 0 ) ) );
1050 
1051  PenFinish();
1052  }
1053 }
1054 
1055 void GERBER_PLOTTER::PlotPoly( const std::vector< wxPoint >& aCornerList, FILL_T aFill, int aWidth,
1056  void * aData )
1057 {
1058  if( aCornerList.size() <= 1 )
1059  return;
1060 
1061  // Gerber format does not know filled polygons with thick outline
1062  // Therefore, to plot a filled polygon with outline having a thickness,
1063  // one should plot outline as thick segments
1064  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1065 
1066  if( gbr_metadata )
1067  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1068 
1069  if( aFill != FILL_T::NO_FILL )
1070  {
1071  fputs( "G36*\n", m_outputFile );
1072 
1073  MoveTo( aCornerList[0] );
1074  fputs( "G01*\n", m_outputFile ); // Set linear interpolation.
1075 
1076  for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1077  LineTo( aCornerList[ii] );
1078 
1079  // If the polygon is not closed, close it:
1080  if( aCornerList[0] != aCornerList[aCornerList.size()-1] )
1081  FinishTo( aCornerList[0] );
1082 
1083  fputs( "G37*\n", m_outputFile );
1084  }
1085 
1086  if( aWidth > 0 ) // Draw the polyline/polygon outline
1087  {
1088  SetCurrentLineWidth( aWidth, gbr_metadata );
1089 
1090  MoveTo( aCornerList[0] );
1091 
1092  for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
1093  LineTo( aCornerList[ii] );
1094 
1095  // Ensure the thick outline is closed for filled polygons
1096  // (if not filled, could be only a polyline)
1097  if( aFill != FILL_T::NO_FILL && ( aCornerList[aCornerList.size() - 1] != aCornerList[0] ) )
1098  LineTo( aCornerList[0] );
1099 
1100  PenFinish();
1101  }
1102 }
1103 
1104 
1105 void GERBER_PLOTTER::ThickSegment( const wxPoint& start, const wxPoint& end, int width,
1106  OUTLINE_MODE tracemode, void* aData )
1107 {
1108  if( tracemode == FILLED )
1109  {
1110  GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1111  SetCurrentLineWidth( width, gbr_metadata );
1112 
1113  if( gbr_metadata )
1114  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1115 
1116  MoveTo( start );
1117  FinishTo( end );
1118  }
1119  else
1120  {
1122  segmentAsOval( start, end, width, tracemode );
1123  }
1124 }
1125 
1126 void GERBER_PLOTTER::ThickArc( const wxPoint& centre, double StAngle, double EndAngle,
1127  int radius, int width, OUTLINE_MODE tracemode, void* aData )
1128 {
1129  GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1130  SetCurrentLineWidth( width, gbr_metadata );
1131 
1132  if( gbr_metadata )
1133  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1134 
1135  if( tracemode == FILLED )
1136  {
1137  Arc( centre, StAngle, EndAngle, radius, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1138  }
1139  else
1140  {
1142  Arc( centre, StAngle, EndAngle, radius - ( width - m_currentPenWidth ) / 2, FILL_T::NO_FILL,
1144  Arc( centre, StAngle, EndAngle, radius + ( width - m_currentPenWidth ) / 2, FILL_T::NO_FILL,
1146  }
1147 }
1148 
1149 
1150 void GERBER_PLOTTER::ThickRect( const wxPoint& p1, const wxPoint& p2, int width,
1151  OUTLINE_MODE tracemode, void* aData )
1152 {
1153  GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1154  SetCurrentLineWidth( width, gbr_metadata );
1155 
1156  if( gbr_metadata )
1157  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1158 
1159  if( tracemode == FILLED )
1160  {
1162  }
1163  else
1164  {
1166  wxPoint offsetp1( p1.x - (width - m_currentPenWidth) / 2,
1167  p1.y - (width - m_currentPenWidth) / 2 );
1168  wxPoint offsetp2( p2.x + (width - m_currentPenWidth) / 2,
1169  p2.y + (width - m_currentPenWidth) / 2 );
1170  Rect( offsetp1, offsetp2, FILL_T::NO_FILL, -1 );
1171  offsetp1.x += (width - m_currentPenWidth);
1172  offsetp1.y += (width - m_currentPenWidth);
1173  offsetp2.x -= (width - m_currentPenWidth);
1174  offsetp2.y -= (width - m_currentPenWidth);
1175  Rect( offsetp1, offsetp2, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1176  }
1177 }
1178 
1179 
1180 void GERBER_PLOTTER::ThickCircle( const wxPoint& pos, int diametre, int width,
1181  OUTLINE_MODE tracemode, void* aData )
1182 {
1183  GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1184  SetCurrentLineWidth( width, gbr_metadata );
1185 
1186  if( gbr_metadata )
1187  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1188 
1189  if( tracemode == FILLED )
1190  {
1191  Circle( pos, diametre, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1192  }
1193  else
1194  {
1196  Circle( pos, diametre - (width - m_currentPenWidth), FILL_T::NO_FILL,
1198  Circle( pos, diametre + (width - m_currentPenWidth), FILL_T::NO_FILL,
1200  }
1201 }
1202 
1203 
1204 void GERBER_PLOTTER::FilledCircle( const wxPoint& pos, int diametre,
1205  OUTLINE_MODE tracemode, void* aData )
1206 {
1207  // A filled circle is a graphic item, not a pad.
1208  // So it is drawn, not flashed.
1209  GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
1210 
1211  if( gbr_metadata )
1212  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1213 
1214  if( tracemode == FILLED )
1215  {
1216  // Draw a circle of diameter = diameter/2 with a line thickness = radius,
1217  // To create a filled circle
1218  SetCurrentLineWidth( diametre/2, gbr_metadata );
1219  Circle( pos, diametre/2, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1220  }
1221  else
1222  {
1224  Circle( pos, diametre, FILL_T::NO_FILL, DO_NOT_SET_LINE_WIDTH );
1225  }
1226 }
1227 
1228 
1229 void GERBER_PLOTTER::FlashPadCircle( const wxPoint& pos, int diametre, OUTLINE_MODE trace_mode,
1230  void* aData )
1231 {
1232  wxSize size( diametre, diametre );
1233  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1234 
1235  if( trace_mode == SKETCH )
1236  {
1237  if( gbr_metadata )
1238  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1239 
1241 
1243  }
1244  else
1245  {
1246  DPOINT pos_dev = userToDeviceCoordinates( pos );
1247 
1248  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1249  selectAperture( size, 0, 0.0, APERTURE::AT_CIRCLE, aperture_attrib );
1250 
1251  if( gbr_metadata )
1252  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1253 
1254  emitDcode( pos_dev, 3 );
1255  }
1256 }
1257 
1258 
1259 void GERBER_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double orient,
1260  OUTLINE_MODE trace_mode, void* aData )
1261 {
1262  wxASSERT( m_outputFile );
1263  wxSize size( aSize );
1264  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1265 
1266  // Flash a vertical or horizontal shape (this is a basic aperture).
1267  if( ( orient == 0 || orient == 900 || orient == 1800 || orient == 2700 )
1268  && trace_mode == FILLED )
1269  {
1270  if( orient == 900 || orient == 2700 ) /* orientation turned 90 deg. */
1271  std::swap( size.x, size.y );
1272 
1273  DPOINT pos_dev = userToDeviceCoordinates( pos );
1274  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1275  selectAperture( size, 0, 0.0, APERTURE::AT_OVAL, aperture_attrib );
1276 
1277  if( gbr_metadata )
1278  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1279 
1280  emitDcode( pos_dev, 3 );
1281  }
1282  else // Plot pad as region.
1283  // Only regions and flashed items accept a object attribute TO.P for the pin name
1284  {
1285  if( trace_mode == FILLED )
1286  {
1287  #ifdef GBR_USE_MACROS_FOR_ROTATED_OVAL
1289  #endif
1290  {
1291  m_hasApertureRotOval = true;
1292  // We are using a aperture macro that expect size.y < size.x
1293  // i.e draw a horizontal line for rotation = 0.0
1294  // size.x = length, size.y = width
1295  if( size.x < size.y )
1296  {
1297  std::swap( size.x, size.y );
1298  orient += 900;
1299 
1300  if( orient > 1800 )
1301  orient -= 1800;
1302  }
1303 
1304  DPOINT pos_dev = userToDeviceCoordinates( pos );
1305  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1306  selectAperture( size, 0, orient/10.0, APERTURE::AM_ROTATED_OVAL, aperture_attrib );
1307 
1308  if( gbr_metadata )
1309  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1310 
1311  emitDcode( pos_dev, 3 );
1312  return;
1313  }
1314  // Draw the oval as round rect pad with a radius = 50% min size)
1315  // In gerber file, it will be drawn as a region with arcs, and can be
1316  // detected as pads (similar to a flashed pad)
1317  FlashPadRoundRect( pos, aSize, std::min( aSize.x, aSize.y ) / 2,
1318  orient, FILLED, aData );
1319  }
1320  else // Non filled shape: plot outlines:
1321  {
1322  if( size.x > size.y )
1323  {
1324  std::swap( size.x, size.y );
1325 
1326  if( orient < 2700 )
1327  orient += 900;
1328  else
1329  orient -= 2700;
1330  }
1331 
1332  sketchOval( pos, size, orient, -1 );
1333  }
1334  }
1335 }
1336 
1337 
1338 void GERBER_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& aSize,
1339  double orient, OUTLINE_MODE trace_mode, void* aData )
1340 
1341 {
1342  wxASSERT( m_outputFile );
1343  wxSize size( aSize );
1344  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1345 
1346  // Plot as an aperture flash
1347  switch( int( orient ) )
1348  {
1349  case 900:
1350  case 2700: // rotation of 90 degrees or 270 swaps sizes
1351  std::swap( size.x, size.y );
1353 
1354  case 0:
1355  case 1800:
1356  if( trace_mode == SKETCH )
1357  {
1358  if( gbr_metadata )
1359  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1360 
1362  Rect( wxPoint( pos.x - (size.x - GetCurrentLineWidth()) / 2,
1363  pos.y - (size.y - GetCurrentLineWidth()) / 2 ),
1364  wxPoint( pos.x + (size.x - GetCurrentLineWidth()) / 2,
1365  pos.y + (size.y - GetCurrentLineWidth()) / 2 ),
1367  }
1368  else
1369  {
1370  DPOINT pos_dev = userToDeviceCoordinates( pos );
1371  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1372  selectAperture( size, 0, 0.0, APERTURE::AT_RECT, aperture_attrib );
1373 
1374  if( gbr_metadata )
1375  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1376 
1377  emitDcode( pos_dev, 3 );
1378  }
1379  break;
1380 
1381  default:
1382  #ifdef GBR_USE_MACROS_FOR_ROTATED_RECT
1383  if( trace_mode != SKETCH && !m_gerberDisableApertMacros )
1384  {
1385  m_hasApertureRotRect = true;
1386  DPOINT pos_dev = userToDeviceCoordinates( pos );
1387  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1388  selectAperture( size, 0, orient/10.0, APERTURE::AM_ROT_RECT, aperture_attrib );
1389 
1390  if( gbr_metadata )
1391  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1392 
1393  emitDcode( pos_dev, 3 );
1394 
1395  break;
1396  }
1397  #endif
1398  {
1399  // plot pad shape as Gerber region
1400  wxPoint coord[4];
1401  // coord[0] is assumed the lower left
1402  // coord[1] is assumed the upper left
1403  // coord[2] is assumed the upper right
1404  // coord[3] is assumed the lower right
1405 
1406  coord[0].x = -size.x/2; // lower left
1407  coord[0].y = size.y/2;
1408  coord[1].x = -size.x/2; // upper left
1409  coord[1].y = -size.y/2;
1410  coord[2].x = size.x/2; // upper right
1411  coord[2].y = -size.y/2;
1412  coord[3].x = size.x/2; // lower right
1413  coord[3].y = size.y/2;
1414 
1415  FlashPadTrapez( pos, coord, orient, trace_mode, aData );
1416  }
1417  break;
1418  }
1419 }
1420 
1421 void GERBER_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSize,
1422  int aCornerRadius, double aOrient,
1423  OUTLINE_MODE aTraceMode, void* aData )
1424 
1425 {
1426  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1427 
1428  if( aTraceMode != FILLED )
1429  {
1430  SHAPE_POLY_SET outline;
1431  TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient, aCornerRadius,
1432  0.0, 0, 0, GetPlotterArcHighDef(), ERROR_INSIDE );
1433 
1434  SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata );
1435  outline.Inflate( -GetCurrentLineWidth()/2, 16 );
1436 
1437  std::vector< wxPoint > cornerList;
1438  // TransformRoundRectToPolygon creates only one convex polygon
1439  SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
1440  cornerList.reserve( poly.PointCount() + 1 );
1441 
1442  for( int ii = 0; ii < poly.PointCount(); ++ii )
1443  cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
1444 
1445  // Close polygon
1446  cornerList.push_back( cornerList[0] );
1447 
1448  // plot outlines
1449  PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), gbr_metadata );
1450  }
1451  else
1452  {
1453  #ifdef GBR_USE_MACROS_FOR_ROUNDRECT
1455  #endif
1456  {
1457  m_hasApertureRoundRect = true;
1458 
1459  DPOINT pos_dev = userToDeviceCoordinates( aPadPos );
1460  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1461  selectAperture( aSize, aCornerRadius, aOrient/10.0,
1462  APERTURE::AM_ROUND_RECT, aperture_attrib );
1463 
1464  if( gbr_metadata )
1465  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1466 
1467  emitDcode( pos_dev, 3 );
1468  return;
1469  }
1470 
1471  // A Pad RoundRect is plotted as a Gerber region.
1472  // Initialize region metadata:
1473  bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
1474 
1475  if( gbr_metadata )
1476  {
1477  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1478  std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
1479 
1480  if( !attrib.empty() )
1481  {
1482  fputs( attrib.c_str(), m_outputFile );
1483  clearTA_AperFunction = true;
1484  }
1485  }
1486 
1487  // Plot the region using arcs in corners.
1488  plotRoundRectAsRegion( aPadPos, aSize, aCornerRadius, aOrient );
1489 
1490  // Clear the TA attribute, to avoid the next item to inherit it:
1491  if( clearTA_AperFunction )
1492  {
1493  if( m_useX2format )
1494  fputs( "%TD.AperFunction*%\n", m_outputFile );
1495  else
1496  fputs( "G04 #@! TD.AperFunction*\n", m_outputFile );
1497  }
1498  }
1499 }
1500 
1501 
1502 void GERBER_PLOTTER::plotRoundRectAsRegion( const wxPoint& aRectCenter, const wxSize& aSize,
1503  int aCornerRadius, double aOrient )
1504 {
1505  // The region outline is generated by 4 sides and 4 90 deg arcs
1506  // 1 --- 2
1507  // | c |
1508  // 4 --- 3
1509 
1510  // Note also in user coordinates the Y axis is from top to bottom
1511  // for historical reasons.
1512 
1513  // A helper structure to handle outlines coordinates (segments and arcs)
1514  // in user coordinates
1515  struct RR_EDGE
1516  {
1517  wxPoint m_start;
1518  wxPoint m_end;
1519  wxPoint m_center;
1520  // in decidegrees: angle start. angle end = m_arc_angle_start+arc_angle
1521  double m_arc_angle_start;
1522  };
1523 
1524  const double arc_angle = -900.0; // in decidegrees
1525  int hsizeX = aSize.x/2;
1526  int hsizeY = aSize.y/2;
1527 
1528  RR_EDGE curr_edge;
1529  std::vector<RR_EDGE> rr_outline;
1530 
1531  // Build outline coordinates, relative to rectangle center, rotation 0:
1532 
1533  // Top left corner 1 (and 4 to 1 left vertical side @ x=-hsizeX)
1534  curr_edge.m_start.x = -hsizeX;
1535  curr_edge.m_start.y = hsizeY - aCornerRadius;
1536  curr_edge.m_end.x = curr_edge.m_start.x;
1537  curr_edge.m_end.y = -hsizeY + aCornerRadius;
1538  curr_edge.m_center.x = -hsizeX + aCornerRadius;
1539  curr_edge.m_center.y = curr_edge.m_end.y;
1540  curr_edge.m_arc_angle_start = aOrient + 1800.0; // En decidegree
1541 
1542  rr_outline.push_back( curr_edge );
1543 
1544  // Top right corner 2 (and 1 to 2 top horizontal side @ y=-hsizeY)
1545  curr_edge.m_start.x = -hsizeX + aCornerRadius;
1546  curr_edge.m_start.y = -hsizeY;
1547  curr_edge.m_end.x = hsizeX - aCornerRadius;
1548  curr_edge.m_end.y = curr_edge.m_start.y;
1549  curr_edge.m_center.x = curr_edge.m_end.x;
1550  curr_edge.m_center.y = -hsizeY + aCornerRadius;
1551  curr_edge.m_arc_angle_start = aOrient + 900.0; // En decidegree
1552 
1553  rr_outline.push_back( curr_edge );
1554 
1555  // bottom right corner 3 (and 2 to 3 right vertical side @ x=hsizeX)
1556  curr_edge.m_start.x = hsizeX;
1557  curr_edge.m_start.y = -hsizeY + aCornerRadius;
1558  curr_edge.m_end.x = curr_edge.m_start.x;
1559  curr_edge.m_end.y = hsizeY - aCornerRadius;
1560  curr_edge.m_center.x = hsizeX - aCornerRadius;
1561  curr_edge.m_center.y = curr_edge.m_end.y;
1562  curr_edge.m_arc_angle_start = aOrient + 0.0; // En decidegree
1563 
1564  rr_outline.push_back( curr_edge );
1565 
1566  // bottom left corner 4 (and 3 to 4 bottom horizontal side @ y=hsizeY)
1567  curr_edge.m_start.x = hsizeX - aCornerRadius;
1568  curr_edge.m_start.y = hsizeY;
1569  curr_edge.m_end.x = -hsizeX + aCornerRadius;
1570  curr_edge.m_end.y = curr_edge.m_start.y;
1571  curr_edge.m_center.x = curr_edge.m_end.x;
1572  curr_edge.m_center.y = hsizeY - aCornerRadius;
1573  curr_edge.m_arc_angle_start = aOrient - 900.0; // En decidegree
1574 
1575  rr_outline.push_back( curr_edge );
1576 
1577  // Move relative coordinates to the actual location and rotation:
1578  wxPoint arc_last_center;
1579  int arc_last_angle = curr_edge.m_arc_angle_start+arc_angle;
1580 
1581  for( RR_EDGE& rr_edge: rr_outline )
1582  {
1583  RotatePoint( &rr_edge.m_start, aOrient );
1584  RotatePoint( &rr_edge.m_end, aOrient );
1585  RotatePoint( &rr_edge.m_center, aOrient );
1586  rr_edge.m_start += aRectCenter;
1587  rr_edge.m_end += aRectCenter;
1588  rr_edge.m_center += aRectCenter;
1589  arc_last_center = rr_edge.m_center;
1590  }
1591 
1592  // Ensure the region is a closed polygon, i.e. the end point of last segment
1593  // (end of arc) is the same as the first point. Rounding issues can create a
1594  // small difference, mainly for rotated pads.
1595  // calculate last point (end of last arc):
1596  wxPoint last_pt;
1597  last_pt.x = arc_last_center.x + KiROUND( cosdecideg( aCornerRadius, arc_last_angle ) );
1598  last_pt.y = arc_last_center.y - KiROUND( sindecideg( aCornerRadius, arc_last_angle ) );
1599 
1600  wxPoint first_pt = rr_outline[0].m_start;
1601 
1602 #if 0 // For test only:
1603  if( last_pt != first_pt )
1604  wxLogMessage( "first pt %d %d last pt %d %d",
1605  first_pt.x, first_pt.y, last_pt.x, last_pt.y );
1606 #endif
1607 
1608  fputs( "G36*\n", m_outputFile ); // Start region
1609  fputs( "G01*\n", m_outputFile ); // Set linear interpolation.
1610  first_pt = last_pt;
1611  MoveTo( first_pt ); // Start point of region, must be same as end point
1612 
1613  for( RR_EDGE& rr_edge: rr_outline )
1614  {
1615  if( aCornerRadius ) // Guard: ensure we do not create arcs with radius = 0
1616  {
1617  // LineTo( rr_edge.m_end ); // made in plotArc()
1618  plotArc( rr_edge.m_center, rr_edge.m_arc_angle_start,
1619  rr_edge.m_arc_angle_start+arc_angle, aCornerRadius, true );
1620  }
1621  else
1622  {
1623  LineTo( rr_edge.m_end );
1624  }
1625  }
1626 
1627  fputs( "G37*\n", m_outputFile ); // Close region
1628 }
1629 
1630 
1631 void GERBER_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize,
1632  double aOrient, SHAPE_POLY_SET* aPolygons,
1633  OUTLINE_MODE aTraceMode, void* aData )
1634 
1635 {
1636  // A Pad custom is plotted as polygon (a region in Gerber language).
1637  GBR_METADATA gbr_metadata;
1638 
1639  if( aData )
1640  gbr_metadata = *static_cast<GBR_METADATA*>( aData );
1641 
1642  SHAPE_POLY_SET polyshape = *aPolygons;
1643 
1644  if( aTraceMode != FILLED )
1645  {
1646  SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata );
1647  polyshape.Inflate( -GetCurrentLineWidth()/2, 16 );
1648  }
1649 
1650  std::vector< wxPoint > cornerList;
1651 
1652  for( int cnt = 0; cnt < polyshape.OutlineCount(); ++cnt )
1653  {
1654  SHAPE_LINE_CHAIN& poly = polyshape.Outline( cnt );
1655 
1656  cornerList.clear();
1657 
1658  for( int ii = 0; ii < poly.PointCount(); ++ii )
1659  cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
1660 
1661  // Close polygon
1662  cornerList.push_back( cornerList[0] );
1663 
1664  if( aTraceMode == SKETCH )
1665  {
1666  PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
1667  }
1668  else
1669  {
1670 #ifdef GBR_USE_MACROS_FOR_CUSTOM_PAD
1672  || cornerList.size() > GBR_MACRO_FOR_CUSTOM_PAD_MAX_CORNER_COUNT )
1673  PlotGerberRegion( cornerList, &gbr_metadata );
1674  else
1675  {
1676  // An AM will be created. the shape must be in position 0,0 and orientation 0
1677  // to be able to reuse the same AM for pads having the same shape
1678  for( size_t ii = 0; ii < cornerList.size(); ii++ )
1679  {
1680  cornerList[ii] -= aPadPos;
1681  RotatePoint( &cornerList[ii], -aOrient );
1682  }
1683 
1684  DPOINT pos_dev = userToDeviceCoordinates( aPadPos );
1685  selectAperture( cornerList, aOrient/10.0,
1686  APERTURE::AM_FREE_POLYGON, gbr_metadata.GetApertureAttrib() );
1687  formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1688 
1689  emitDcode( pos_dev, 3 );
1690  }
1691 #else
1692  PlotGerberRegion( cornerList, &gbr_metadata );
1693 #endif
1694  }
1695  }
1696 }
1697 
1698 
1699 void GERBER_PLOTTER::FlashPadChamferRoundRect( const wxPoint& aShapePos, const wxSize& aPadSize,
1700  int aCornerRadius, double aChamferRatio,
1701  int aChamferPositions, double aPadOrient,
1702  OUTLINE_MODE aPlotMode, void* aData )
1703 
1704 {
1705  GBR_METADATA gbr_metadata;
1706 
1707  if( aData )
1708  gbr_metadata = *static_cast<GBR_METADATA*>( aData );
1709 
1710  DPOINT pos_dev = userToDeviceCoordinates( aShapePos );
1711 
1712  SHAPE_POLY_SET outline;
1713  // polygon corners list
1714  std::vector<wxPoint> cornerList;
1715 
1716  bool hasRoundedCorner = aCornerRadius != 0 && aChamferPositions != 15;
1717 
1718 #ifdef GBR_USE_MACROS_FOR_CHAMFERED_RECT
1719  // Sketch mode or round rect shape or Apert Macros disabled
1720  if( aPlotMode != FILLED || hasRoundedCorner || m_gerberDisableApertMacros )
1721 #endif
1722  {
1723  TransformRoundChamferedRectToPolygon( outline, aShapePos, aPadSize, aPadOrient,
1724  aCornerRadius, aChamferRatio, aChamferPositions, 0,
1726 
1727  // Build the corner list
1728  const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
1729 
1730  for( int ii = 0; ii < corners.PointCount(); ii++ )
1731  cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
1732 
1733  // Close the polygon
1734  cornerList.push_back( cornerList[0] );
1735 
1736  if( aPlotMode == SKETCH )
1737  PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
1738  else
1739  {
1740 #ifdef GBR_USE_MACROS_FOR_CHAMFERED_ROUND_RECT
1742  {
1743  PlotGerberRegion( cornerList, &gbr_metadata );
1744  }
1745  else
1746  {
1747  // An AM will be created. the shape must be in position 0,0 and orientation 0
1748  // to be able to reuse the same AM for pads having the same shape
1749  for( size_t ii = 0; ii < cornerList.size(); ii++ )
1750  {
1751  cornerList[ii] -= aShapePos;
1752  RotatePoint( &cornerList[ii], -aPadOrient );
1753  }
1754 
1755  selectAperture( cornerList, aPadOrient/10.0,
1756  APERTURE::AM_FREE_POLYGON, gbr_metadata.GetApertureAttrib() );
1757  formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1758 
1759  emitDcode( pos_dev, 3 );
1760  }
1761 #else
1762  PlotGerberRegion( cornerList, &gbr_metadata );
1763 #endif
1764  }
1765 
1766  return;
1767  }
1768 
1769  // Build the chamfered polygon (4 to 8 corners )
1770  TransformRoundChamferedRectToPolygon( outline, wxPoint( 0, 0 ), aPadSize, 0.0, 0,
1771  aChamferRatio, aChamferPositions, 0,
1773 
1774  // Build the corner list
1775  const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
1776 
1777  // Generate the polygon (4 to 8 corners )
1778  for( int ii = 0; ii < corners.PointCount(); ii++ )
1779  cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
1780 
1781  switch( cornerList.size() )
1782  {
1783  case 4:
1784  m_hasApertureOutline4P = true;
1785  selectAperture( cornerList, aPadOrient/10.0,
1787  break;
1788 
1789  case 5:
1791  selectAperture( cornerList, aPadOrient/10.0,
1793  break;
1794 
1795  case 6:
1797  selectAperture( cornerList, aPadOrient/10.0,
1799  break;
1800 
1801  case 7:
1803  selectAperture( cornerList, aPadOrient/10.0,
1805  break;
1806 
1807  case 8:
1809  selectAperture( cornerList, aPadOrient/10.0,
1811  break;
1812 
1813  default:
1814  wxLogMessage( "FlashPadChamferRoundRect(): Unexpected number of corners (%d)",
1815  (int)cornerList.size() );
1816  break;
1817  }
1818 
1819  formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
1820 
1821  emitDcode( pos_dev, 3 );
1822 }
1823 
1824 
1825 void GERBER_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCorners,
1826  double aPadOrient, OUTLINE_MODE aTrace_Mode, void* aData )
1827 
1828 {
1829  // polygon corners list
1830  std::vector<wxPoint> cornerList = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
1831 
1832  // Draw the polygon and fill the interior as required
1833  for( unsigned ii = 0; ii < 4; ii++ )
1834  {
1835  RotatePoint( &cornerList[ii], aPadOrient );
1836  cornerList[ii] += aPadPos;
1837  }
1838 
1839  // Close the polygon
1840  cornerList.push_back( cornerList[0] );
1841  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1842 
1843  GBR_METADATA metadata;
1844 
1845  if( gbr_metadata )
1846  metadata = *gbr_metadata;
1847 
1848  if( aTrace_Mode == SKETCH )
1849  {
1850  PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), &metadata );
1851  return;
1852  }
1853 
1854  // Plot a filled polygon:
1855  #ifdef GBR_USE_MACROS_FOR_TRAPEZOID
1857  #endif
1858  {
1859  m_hasApertureOutline4P = true;
1860  DPOINT pos_dev = userToDeviceCoordinates( aPadPos );
1861  // polygon corners list
1862  std::vector<wxPoint> corners = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
1863  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1864  selectAperture( corners, aPadOrient/10.0, APERTURE::APER_MACRO_OUTLINE4P, aperture_attrib );
1865 
1866  if( gbr_metadata )
1867  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1868 
1869  emitDcode( pos_dev, 3 );
1870  return;
1871  }
1872 
1873  PlotGerberRegion( cornerList, &metadata );
1874 }
1875 
1876 
1877 void GERBER_PLOTTER::FlashRegularPolygon( const wxPoint& aShapePos, int aDiameter,
1878  int aCornerCount, double aOrient,
1879  OUTLINE_MODE aTraceMode, void* aData )
1880 {
1881  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1882 
1883  GBR_METADATA metadata;
1884 
1885  if( gbr_metadata )
1886  metadata = *gbr_metadata;
1887 
1888  if( aTraceMode == SKETCH )
1889  {
1890  // Build the polygon:
1891  std::vector< wxPoint > cornerList;
1892 
1893  double angle_delta = 3600.0 / aCornerCount; // in 0.1 degree
1894 
1895  for( int ii = 0; ii < aCornerCount; ii++ )
1896  {
1897  double rot = aOrient + (angle_delta*ii);
1898  wxPoint vertice( aDiameter/2, 0 );
1899  RotatePoint( &vertice, rot );
1900  vertice += aShapePos;
1901  cornerList.push_back( vertice );
1902  }
1903 
1904  cornerList.push_back( cornerList[0] ); // Close the shape
1905 
1906  PlotPoly( cornerList, FILL_T::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
1907  }
1908  else
1909  {
1910  DPOINT pos_dev = userToDeviceCoordinates( aShapePos );
1911 
1912  int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
1913 
1914  APERTURE::APERTURE_TYPE apert_type =
1916  selectAperture( aDiameter, aOrient, apert_type, aperture_attrib );
1917 
1918  if( gbr_metadata )
1919  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1920 
1921  emitDcode( pos_dev, 3 );
1922  }
1923 }
1924 
1925 
1926 void GERBER_PLOTTER::Text( const wxPoint& aPos, const COLOR4D& aColor,
1927  const wxString& aText, double aOrient, const wxSize& aSize,
1928  enum EDA_TEXT_HJUSTIFY_T aH_justify,
1929  enum EDA_TEXT_VJUSTIFY_T aV_justify, int aWidth, bool aItalic,
1930  bool aBold, bool aMultilineAllowed, void* aData )
1931 {
1932  GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
1933 
1934  if( gbr_metadata )
1935  formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
1936 
1937  PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth, aItalic,
1938  aBold, aMultilineAllowed, aData );
1939 }
1940 
1941 
1942 void GERBER_PLOTTER::SetLayerPolarity( bool aPositive )
1943 {
1944  if( aPositive )
1945  fprintf( m_outputFile, "%%LPD*%%\n" );
1946  else
1947  fprintf( m_outputFile, "%%LPC*%%\n" );
1948 }
1949 
1950 
1951 bool APER_MACRO_FREEPOLY::IsSamePoly( const std::vector<wxPoint>& aPolygon ) const
1952 {
1953  return polyCompare( m_Corners, aPolygon );
1954 }
1955 
1956 
1957 void APER_MACRO_FREEPOLY::Format( FILE * aOutput, double aIu2GbrMacroUnit )
1958 {
1959  // Write aperture header
1960  fprintf( aOutput, "%%AM%s%d*\n", AM_FREEPOLY_BASENAME, m_Id );
1961  fprintf( aOutput, "4,1,%d,", (int)m_Corners.size() );
1962 
1963  // Insert a newline after curr_line_count_max coordinates.
1964  int curr_line_corner_count = 0;
1965  const int curr_line_count_max = 20; // <= 0 to disable newlines
1966 
1967  for( size_t ii = 0; ii <= m_Corners.size(); ii++ )
1968  {
1969  int jj = ii;
1970 
1971  if( ii >= m_Corners.size() )
1972  jj = 0;
1973 
1974  // Note: parameter values are always mm or inches
1975  fprintf( aOutput, "%#f,%#f,",
1976  m_Corners[jj].x * aIu2GbrMacroUnit, -m_Corners[jj].y * aIu2GbrMacroUnit );
1977 
1978  if( curr_line_count_max >= 0 && ++curr_line_corner_count >= curr_line_count_max )
1979  {
1980  fprintf( aOutput, "\n" );
1981  curr_line_corner_count = 0;
1982  }
1983  }
1984 
1985  // output rotation parameter
1986  fputs( "$1*%\n", aOutput );
1987 }
1988 
1989 
1990 void APER_MACRO_FREEPOLY_LIST::Format( FILE * aOutput, double aIu2GbrMacroUnit )
1991 {
1992  for( int idx = 0; idx < AmCount(); idx++ )
1993  m_AMList[idx].Format( aOutput, aIu2GbrMacroUnit );
1994 }
1995 
1996 
1997 void APER_MACRO_FREEPOLY_LIST::Append( const std::vector<wxPoint>& aPolygon )
1998 {
1999  m_AMList.emplace_back( aPolygon, AmCount() );
2000 }
2001 
2002 
2003 int APER_MACRO_FREEPOLY_LIST::FindAm( const std::vector<wxPoint>& aPolygon ) const
2004 {
2005  for( int idx = 0; idx < AmCount(); idx++ )
2006  {
2007  if( m_AMList[idx].IsSamePoly( aPolygon ) )
2008  return idx;
2009  }
2010 
2011  return -1;
2012 }
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:460
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.
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:444
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:52
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.
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
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:454
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: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 (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: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:481
const VECTOR2I & GetP1() const
Definition: shape_arc.h:112
VECTOR2I GetCenter() const
Definition: shape_arc.cpp:464
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