You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1844 lines
63 KiB

17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
  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. /**
  21. * @file GERBER_plotter.cpp
  22. * @brief specialized plotter for GERBER files format
  23. */
  24. #include <eda_base_frame.h>
  25. #include <fill_type.h>
  26. #include <kicad_string.h>
  27. #include <convert_basic_shapes_to_polygon.h>
  28. #include <macros.h>
  29. #include <math/util.h> // for KiROUND
  30. #include <render_settings.h>
  31. #include <trigo.h>
  32. #include <build_version.h>
  33. #include "plotter_gerber.h"
  34. #include "gbr_plotter_aperture_macros.h"
  35. #include <gbr_metadata.h>
  36. // if GBR_USE_MACROS is defined, pads having a shape that is not a Gerber primitive
  37. // will use a macro when possible
  38. // Old code will be removed only after many tests
  39. //
  40. // Note also: setting m_gerberDisableApertMacros to true disable all aperture macros
  41. // in Gerber files
  42. //
  43. #define GBR_USE_MACROS_FOR_CHAMFERED_ROUND_RECT
  44. #define GBR_USE_MACROS_FOR_CHAMFERED_RECT
  45. #define GBR_USE_MACROS_FOR_ROUNDRECT
  46. #define GBR_USE_MACROS_FOR_TRAPEZOID
  47. #define GBR_USE_MACROS_FOR_ROTATED_OVAL
  48. #define GBR_USE_MACROS_FOR_ROTATED_RECT
  49. #define GBR_USE_MACROS_FOR_CUSTOM_PAD
  50. // max count of corners to create a aperture macro for a custom shape.
  51. // provided just in case a aperture macro type free polygon creates issues
  52. // when the number of corners is too high.
  53. // (1 corner = up to 24 chars)
  54. // Gerber doc say max corners 5000. We use a slightly smaller value.
  55. // if a custom shape needs more than GBR_MACRO_FOR_CUSTOM_PAD_MAX_CORNER_COUNT, it
  56. // will be plot using a region.
  57. #define GBR_MACRO_FOR_CUSTOM_PAD_MAX_CORNER_COUNT 4990
  58. #define AM_FREEPOLY_BASENAME "FreePoly"
  59. // A helper function to compare 2 polygons: polygons are similar if they havve the same
  60. // number of vertices and each vertex coordinate are similar, i.e. if the difference
  61. // between coordinates is small ( <= margin to accept rounding issues coming from polygon
  62. // geometric transforms like rotation
  63. static bool polyCompare( const std::vector<wxPoint>& aPolygon,
  64. const std::vector<wxPoint>& aTestPolygon )
  65. {
  66. // fast test: polygon sizes must be the same:
  67. if( aTestPolygon.size() != aPolygon.size() )
  68. return false;
  69. const int margin = 2;
  70. for( size_t jj = 0; jj < aPolygon.size(); jj++ )
  71. {
  72. if( std::abs( aPolygon[jj].x - aTestPolygon[jj].x ) > margin ||
  73. std::abs( aPolygon[jj].y - aTestPolygon[jj].y ) > margin )
  74. return false;
  75. }
  76. return true;
  77. }
  78. GERBER_PLOTTER::GERBER_PLOTTER()
  79. {
  80. workFile = nullptr;
  81. finalFile = nullptr;
  82. m_currentApertureIdx = -1;
  83. m_apertureAttribute = 0;
  84. // number of digits after the point (number of digits of the mantissa
  85. // Be careful: the Gerber coordinates are stored in an integer
  86. // so 6 digits (inches) or 5 digits (mm) is a good value
  87. // To avoid overflow, 7 digits (inches) or 6 digits is a max.
  88. // with lower values than 6 digits (inches) or 5 digits (mm),
  89. // Creating self-intersecting polygons from non-intersecting polygons
  90. // happen easily.
  91. m_gerberUnitInch = false;
  92. m_gerberUnitFmt = 6;
  93. m_useX2format = true;
  94. m_useNetAttributes = true;
  95. m_gerberDisableApertMacros = false;
  96. m_hasApertureRoundRect = false; // true is at least one round rect aperture is in use
  97. m_hasApertureRotOval = false; // true is at least one oval rotated aperture is in use
  98. m_hasApertureRotRect = false; // true is at least one rect. rotated aperture is in use
  99. m_hasApertureOutline4P = false; // true is at least one rotated rect or trapezoid pad
  100. // aperture is in use
  101. m_hasApertureChamferedRect = false; // true is at least one chamfered rect
  102. // (no rounded corner) is in use
  103. }
  104. void GERBER_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil,
  105. double aScale, bool aMirror )
  106. {
  107. wxASSERT( aMirror == false );
  108. m_plotMirror = false;
  109. m_plotOffset = aOffset;
  110. wxASSERT( aScale == 1 ); // aScale parameter is not used in Gerber
  111. m_plotScale = 1; // Plot scale is *always* 1.0
  112. m_IUsPerDecimil = aIusPerDecimil;
  113. // gives now a default value to iuPerDeviceUnit (because the units of the caller is now known)
  114. // which could be modified later by calling SetGerberCoordinatesFormat()
  115. m_iuPerDeviceUnit = pow( 10.0, m_gerberUnitFmt ) / ( m_IUsPerDecimil * 10000.0 );
  116. // We don't handle the filmbox, and it's more useful to keep the
  117. // origin at the origin
  118. m_paperSize.x = 0;
  119. m_paperSize.y = 0;
  120. }
  121. void GERBER_PLOTTER::SetGerberCoordinatesFormat( int aResolution, bool aUseInches )
  122. {
  123. m_gerberUnitInch = aUseInches;
  124. m_gerberUnitFmt = aResolution;
  125. m_iuPerDeviceUnit = pow( 10.0, m_gerberUnitFmt ) / ( m_IUsPerDecimil * 10000.0 );
  126. if( ! m_gerberUnitInch )
  127. m_iuPerDeviceUnit *= 25.4; // gerber output in mm
  128. }
  129. void GERBER_PLOTTER::emitDcode( const DPOINT& pt, int dcode )
  130. {
  131. fprintf( m_outputFile, "X%dY%dD%02d*\n", KiROUND( pt.x ), KiROUND( pt.y ), dcode );
  132. }
  133. void GERBER_PLOTTER::ClearAllAttributes()
  134. {
  135. // Remove all attributes from object attributes dictionary (TO. and TA commands)
  136. if( m_useX2format )
  137. fputs( "%TD*%\n", m_outputFile );
  138. else
  139. fputs( "G04 #@! TD*\n", m_outputFile );
  140. m_objectAttributesDictionary.clear();
  141. }
  142. void GERBER_PLOTTER::clearNetAttribute()
  143. {
  144. // disable a Gerber net attribute (exists only in X2 with net attributes mode).
  145. if( m_objectAttributesDictionary.empty() ) // No net attribute or not X2 mode
  146. return;
  147. // Remove all net attributes from object attributes dictionary
  148. if( m_useX2format )
  149. fputs( "%TD*%\n", m_outputFile );
  150. else
  151. fputs( "G04 #@! TD*\n", m_outputFile );
  152. m_objectAttributesDictionary.clear();
  153. }
  154. void GERBER_PLOTTER::StartBlock( void* aData )
  155. {
  156. // Currently, it is the same as EndBlock(): clear all aperture net attributes
  157. EndBlock( aData );
  158. }
  159. void GERBER_PLOTTER::EndBlock( void* aData )
  160. {
  161. // Remove all net attributes from object attributes dictionary
  162. clearNetAttribute();
  163. }
  164. void GERBER_PLOTTER::formatNetAttribute( GBR_NETLIST_METADATA* aData )
  165. {
  166. // print a Gerber net attribute record.
  167. // it is added to the object attributes dictionary
  168. // On file, only modified or new attributes are printed.
  169. if( aData == nullptr )
  170. return;
  171. if( !m_useNetAttributes )
  172. return;
  173. bool useX1StructuredComment = !m_useX2format;
  174. bool clearDict;
  175. std::string short_attribute_string;
  176. if( !FormatNetAttribute( short_attribute_string, m_objectAttributesDictionary,
  177. aData, clearDict, useX1StructuredComment ) )
  178. return;
  179. if( clearDict )
  180. clearNetAttribute();
  181. if( !short_attribute_string.empty() )
  182. fputs( short_attribute_string.c_str(), m_outputFile );
  183. if( m_useX2format && !aData->m_ExtraData.IsEmpty() )
  184. {
  185. std::string extra_data = TO_UTF8( aData->m_ExtraData );
  186. fputs( extra_data.c_str(), m_outputFile );
  187. }
  188. }
  189. bool GERBER_PLOTTER::StartPlot()
  190. {
  191. m_hasApertureRoundRect = false; // true is at least one round rect aperture is in use
  192. m_hasApertureRotOval = false; // true is at least one oval rotated aperture is in use
  193. m_hasApertureRotRect = false; // true is at least one rect. rotated aperture is in use
  194. m_hasApertureOutline4P = false; // true is at least one rotated rect/trapezoid aperture is in use
  195. m_hasApertureChamferedRect = false; // true is at least one chamfered rect is in use
  196. m_am_freepoly_list.ClearList();
  197. wxASSERT( m_outputFile );
  198. finalFile = m_outputFile; // the actual gerber file will be created later
  199. // Create a temp file in system temp to avoid potential network share buffer issues for the final read and save
  200. m_workFilename = wxFileName::CreateTempFileName( "" );
  201. workFile = wxFopen( m_workFilename, wxT( "wt" ));
  202. m_outputFile = workFile;
  203. wxASSERT( m_outputFile );
  204. if( m_outputFile == nullptr )
  205. return false;
  206. for( unsigned ii = 0; ii < m_headerExtraLines.GetCount(); ii++ )
  207. {
  208. if( ! m_headerExtraLines[ii].IsEmpty() )
  209. fprintf( m_outputFile, "%s\n", TO_UTF8( m_headerExtraLines[ii] ) );
  210. }
  211. // Set coordinate format to 3.6 or 4.5 absolute, leading zero omitted
  212. // the number of digits for the integer part of coordinates is needed
  213. // in gerber format, but is not very important when omitting leading zeros
  214. // It is fixed here to 3 (inch) or 4 (mm), but is not actually used
  215. int leadingDigitCount = m_gerberUnitInch ? 3 : 4;
  216. fprintf( m_outputFile, "%%FSLAX%d%dY%d%d*%%\n",
  217. leadingDigitCount, m_gerberUnitFmt,
  218. leadingDigitCount, m_gerberUnitFmt );
  219. fprintf( m_outputFile,
  220. "G04 Gerber Fmt %d.%d, Leading zero omitted, Abs format (unit %s)*\n",
  221. leadingDigitCount, m_gerberUnitFmt,
  222. m_gerberUnitInch ? "inch" : "mm" );
  223. wxString Title = m_creator + wxT( " " ) + GetBuildVersion();
  224. // In gerber files, ASCII7 chars only are allowed.
  225. // So use a ISO date format (using a space as separator between date and time),
  226. // not a localized date format
  227. wxDateTime date = wxDateTime::Now();
  228. fprintf( m_outputFile, "G04 Created by KiCad (%s) date %s*\n",
  229. TO_UTF8( Title ), TO_UTF8( date.FormatISOCombined( ' ') ) );
  230. /* Mass parameter: unit = INCHES/MM */
  231. if( m_gerberUnitInch )
  232. fputs( "%MOIN*%\n", m_outputFile );
  233. else
  234. fputs( "%MOMM*%\n", m_outputFile );
  235. // Be sure the usual dark polarity is selected:
  236. fputs( "%LPD*%\n", m_outputFile );
  237. // Set initial interpolation mode: always G01 (linear):
  238. fputs( "G01*\n", m_outputFile );
  239. // Add aperture list start point
  240. fputs( "G04 APERTURE LIST*\n", m_outputFile );
  241. // Give a minimal value to the default pen size, used to plot items in sketch mode
  242. if( m_renderSettings )
  243. {
  244. const int pen_min = 0.1 * m_IUsPerDecimil * 10000 / 25.4; // for min width = 0.1 mm
  245. m_renderSettings->SetDefaultPenWidth( std::max( m_renderSettings->GetDefaultPenWidth(),
  246. pen_min ) );
  247. }
  248. return true;
  249. }
  250. bool GERBER_PLOTTER::EndPlot()
  251. {
  252. char line[1024];
  253. wxString msg;
  254. wxASSERT( m_outputFile );
  255. /* Outfile is actually a temporary file i.e. workFile */
  256. fputs( "M02*\n", m_outputFile );
  257. fflush( m_outputFile );
  258. fclose( workFile );
  259. workFile = wxFopen( m_workFilename, wxT( "rt" ));
  260. wxASSERT( workFile );
  261. m_outputFile = finalFile;
  262. // Placement of apertures in RS274X
  263. while( fgets( line, 1024, workFile ) )
  264. {
  265. fputs( line, m_outputFile );
  266. char* substr = strtok( line, "\n\r" );
  267. if( substr && strcmp( substr, "G04 APERTURE LIST*" ) == 0 )
  268. {
  269. // Add aperture list macro:
  270. if( m_hasApertureRoundRect | m_hasApertureRotOval ||
  271. m_hasApertureOutline4P || m_hasApertureRotRect ||
  272. m_hasApertureChamferedRect || m_am_freepoly_list.AmCount() )
  273. {
  274. fputs( "G04 Aperture macros list*\n", m_outputFile );
  275. if( m_hasApertureRoundRect )
  276. fputs( APER_MACRO_ROUNDRECT_HEADER, m_outputFile );
  277. if( m_hasApertureRotOval )
  278. fputs( APER_MACRO_SHAPE_OVAL_HEADER, m_outputFile );
  279. if( m_hasApertureRotRect )
  280. fputs( APER_MACRO_ROT_RECT_HEADER, m_outputFile );
  281. if( m_hasApertureOutline4P )
  282. fputs( APER_MACRO_OUTLINE4P_HEADER, m_outputFile );
  283. if( m_hasApertureChamferedRect )
  284. {
  285. fputs( APER_MACRO_OUTLINE5P_HEADER, m_outputFile );
  286. fputs( APER_MACRO_OUTLINE6P_HEADER, m_outputFile );
  287. fputs( APER_MACRO_OUTLINE7P_HEADER, m_outputFile );
  288. fputs( APER_MACRO_OUTLINE8P_HEADER, m_outputFile );
  289. }
  290. if( m_am_freepoly_list.AmCount() )
  291. {
  292. // apertude sizes are in inch or mm, regardless the
  293. // coordinates format
  294. double fscale = 0.0001 * m_plotScale / m_IUsPerDecimil; // inches
  295. if(! m_gerberUnitInch )
  296. fscale *= 25.4; // size in mm
  297. m_am_freepoly_list.Format( m_outputFile, fscale );
  298. }
  299. fputs( "G04 Aperture macros list end*\n", m_outputFile );
  300. }
  301. writeApertureList();
  302. fputs( "G04 APERTURE END LIST*\n", m_outputFile );
  303. }
  304. }
  305. fclose( workFile );
  306. fclose( finalFile );
  307. ::wxRemoveFile( m_workFilename );
  308. m_outputFile = nullptr;
  309. return true;
  310. }
  311. void GERBER_PLOTTER::SetCurrentLineWidth( int aWidth, void* aData )
  312. {
  313. if( aWidth == DO_NOT_SET_LINE_WIDTH )
  314. return;
  315. else if( aWidth == USE_DEFAULT_LINE_WIDTH )
  316. aWidth = m_renderSettings->GetDefaultPenWidth();
  317. wxASSERT_MSG( aWidth >= 0, "Plotter called to set negative pen width" );
  318. GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
  319. int aperture_attribute = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
  320. selectAperture( wxSize( aWidth, aWidth ), 0, 0.0, APERTURE::AT_PLOTTING, aperture_attribute );
  321. m_currentPenWidth = aWidth;
  322. }
  323. int GERBER_PLOTTER::GetOrCreateAperture( const wxSize& aSize, int aRadius, double aRotDegree,
  324. APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
  325. {
  326. int last_D_code = 9;
  327. // Search an existing aperture
  328. for( int idx = 0; idx < (int)m_apertures.size(); ++idx )
  329. {
  330. APERTURE* tool = &m_apertures[idx];
  331. last_D_code = tool->m_DCode;
  332. if( (tool->m_Type == aType) && (tool->m_Size == aSize) &&
  333. (tool->m_Radius == aRadius) && (tool->m_Rotation == aRotDegree) &&
  334. (tool->m_ApertureAttribute == aApertureAttribute) )
  335. return idx;
  336. }
  337. // Allocate a new aperture
  338. APERTURE new_tool;
  339. new_tool.m_Size = aSize;
  340. new_tool.m_Type = aType;
  341. new_tool.m_Radius = aRadius;
  342. new_tool.m_Rotation = aRotDegree;
  343. new_tool.m_DCode = last_D_code + 1;
  344. new_tool.m_ApertureAttribute = aApertureAttribute;
  345. m_apertures.push_back( new_tool );
  346. return m_apertures.size() - 1;
  347. }
  348. int GERBER_PLOTTER::GetOrCreateAperture( const std::vector<wxPoint>& aCorners, double aRotDegree,
  349. APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
  350. {
  351. int last_D_code = 9;
  352. // For APERTURE::AM_FREE_POLYGON aperture macros, we need to create the macro
  353. // on the fly, because due to the fact the vertice count is not a constant we
  354. // cannot create a static definition.
  355. if( APERTURE::AM_FREE_POLYGON == aType )
  356. {
  357. int idx = m_am_freepoly_list.FindAm( aCorners );
  358. if( idx < 0 )
  359. m_am_freepoly_list.Append( aCorners );
  360. }
  361. // Search an existing aperture
  362. for( int idx = 0; idx < (int)m_apertures.size(); ++idx )
  363. {
  364. APERTURE* tool = &m_apertures[idx];
  365. last_D_code = tool->m_DCode;
  366. if( (tool->m_Type == aType) &&
  367. (tool->m_Corners.size() == aCorners.size() ) &&
  368. (tool->m_Rotation == aRotDegree) &&
  369. (tool->m_ApertureAttribute == aApertureAttribute) )
  370. {
  371. // A candidate is found. the corner lists must be similar
  372. bool is_same = polyCompare( tool->m_Corners, aCorners );
  373. if( is_same )
  374. return idx;
  375. }
  376. }
  377. // Allocate a new aperture
  378. APERTURE new_tool;
  379. new_tool.m_Corners = aCorners;
  380. new_tool.m_Size = wxSize( 0, 0 ); // Not used
  381. new_tool.m_Type = aType;
  382. new_tool.m_Radius = 0; // Not used
  383. new_tool.m_Rotation = aRotDegree;
  384. new_tool.m_DCode = last_D_code + 1;
  385. new_tool.m_ApertureAttribute = aApertureAttribute;
  386. m_apertures.push_back( new_tool );
  387. return m_apertures.size() - 1;
  388. }
  389. void GERBER_PLOTTER::selectAperture( const wxSize& aSize, int aRadius, double aRotDegree,
  390. APERTURE::APERTURE_TYPE aType,
  391. int aApertureAttribute )
  392. {
  393. bool change = ( m_currentApertureIdx < 0 ) ||
  394. ( m_apertures[m_currentApertureIdx].m_Type != aType ) ||
  395. ( m_apertures[m_currentApertureIdx].m_Size != aSize ) ||
  396. ( m_apertures[m_currentApertureIdx].m_Radius != aRadius ) ||
  397. ( m_apertures[m_currentApertureIdx].m_Rotation != aRotDegree );
  398. if( !change )
  399. change = m_apertures[m_currentApertureIdx].m_ApertureAttribute != aApertureAttribute;
  400. if( change )
  401. {
  402. // Pick an existing aperture or create a new one
  403. m_currentApertureIdx = GetOrCreateAperture( aSize, aRadius, aRotDegree,
  404. aType, aApertureAttribute );
  405. fprintf( m_outputFile, "D%d*\n", m_apertures[m_currentApertureIdx].m_DCode );
  406. }
  407. }
  408. void GERBER_PLOTTER::selectAperture( const std::vector<wxPoint>& aCorners, double aRotDegree,
  409. APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
  410. {
  411. bool change = ( m_currentApertureIdx < 0 ) ||
  412. ( m_apertures[m_currentApertureIdx].m_Type != aType ) ||
  413. ( m_apertures[m_currentApertureIdx].m_Corners.size() != aCorners.size() ) ||
  414. ( m_apertures[m_currentApertureIdx].m_Rotation != aRotDegree );
  415. if( !change ) // Compare corner lists
  416. {
  417. for( size_t ii = 0; ii < aCorners.size(); ii++ )
  418. {
  419. if( aCorners[ii] != m_apertures[m_currentApertureIdx].m_Corners[ii] )
  420. {
  421. change = true;
  422. break;
  423. }
  424. }
  425. }
  426. if( !change )
  427. change = m_apertures[m_currentApertureIdx].m_ApertureAttribute != aApertureAttribute;
  428. if( change )
  429. {
  430. // Pick an existing aperture or create a new one
  431. m_currentApertureIdx = GetOrCreateAperture( aCorners, aRotDegree,
  432. aType, aApertureAttribute );
  433. fprintf( m_outputFile, "D%d*\n", m_apertures[m_currentApertureIdx].m_DCode );
  434. }
  435. }
  436. void GERBER_PLOTTER::selectAperture( int aDiameter, double aPolygonRotation,
  437. APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
  438. {
  439. // Pick an existing aperture or create a new one, matching the
  440. // aDiameter, aPolygonRotation, type and attributes for type =
  441. // AT_REGULAR_POLY3 to AT_REGULAR_POLY12
  442. wxASSERT( aType>= APERTURE::APERTURE_TYPE::AT_REGULAR_POLY3 &&
  443. aType <= APERTURE::APERTURE_TYPE::AT_REGULAR_POLY12 );
  444. wxSize size( aDiameter, (int)( aPolygonRotation * 1000.0 ) );
  445. selectAperture( wxSize( 0, 0), aDiameter/2, aPolygonRotation, aType, aApertureAttribute );
  446. }
  447. void GERBER_PLOTTER::writeApertureList()
  448. {
  449. wxASSERT( m_outputFile );
  450. char cbuf[1024];
  451. std::string buffer;
  452. bool useX1StructuredComment = false;
  453. if( !m_useX2format )
  454. useX1StructuredComment = true;
  455. // Init
  456. for( APERTURE& tool : m_apertures )
  457. {
  458. // apertude sizes are in inch or mm, regardless the
  459. // coordinates format
  460. double fscale = 0.0001 * m_plotScale / m_IUsPerDecimil; // inches
  461. if(! m_gerberUnitInch )
  462. fscale *= 25.4; // size in mm
  463. int attribute = tool.m_ApertureAttribute;
  464. if( attribute != m_apertureAttribute )
  465. {
  466. fputs( GBR_APERTURE_METADATA::FormatAttribute(
  467. (GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB) attribute,
  468. useX1StructuredComment ).c_str(), m_outputFile );
  469. }
  470. sprintf( cbuf, "%%ADD%d", tool.m_DCode );
  471. buffer = cbuf;
  472. /* Please note: the Gerber specs for mass parameters say that
  473. exponential syntax is *not* allowed and the decimal point should
  474. also be always inserted. So the %g format is ruled out, but %f is fine
  475. (the # modifier forces the decimal point). Sadly the %f formatter
  476. can't remove trailing zeros but thats not a problem, since nothing
  477. forbid it (the file is only slightly longer) */
  478. switch( tool.m_Type )
  479. {
  480. case APERTURE::AT_CIRCLE:
  481. sprintf( cbuf, "C,%#f*%%\n", tool.GetDiameter() * fscale );
  482. break;
  483. case APERTURE::AT_RECT:
  484. sprintf( cbuf, "R,%#fX%#f*%%\n", tool.m_Size.x * fscale,
  485. tool.m_Size.y * fscale );
  486. break;
  487. case APERTURE::AT_PLOTTING:
  488. sprintf( cbuf, "C,%#f*%%\n", tool.m_Size.x * fscale );
  489. break;
  490. case APERTURE::AT_OVAL:
  491. sprintf( cbuf, "O,%#fX%#f*%%\n", tool.m_Size.x * fscale,
  492. tool.m_Size.y * fscale );
  493. break;
  494. case APERTURE::AT_REGULAR_POLY:
  495. case APERTURE::AT_REGULAR_POLY3:
  496. case APERTURE::AT_REGULAR_POLY4:
  497. case APERTURE::AT_REGULAR_POLY5:
  498. case APERTURE::AT_REGULAR_POLY6:
  499. case APERTURE::AT_REGULAR_POLY7:
  500. case APERTURE::AT_REGULAR_POLY8:
  501. case APERTURE::AT_REGULAR_POLY9:
  502. case APERTURE::AT_REGULAR_POLY10:
  503. case APERTURE::AT_REGULAR_POLY11:
  504. case APERTURE::AT_REGULAR_POLY12:
  505. sprintf( cbuf, "P,%#fX%dX%#f*%%\n", tool.GetDiameter() * fscale,
  506. tool.GetRegPolyVerticeCount(), tool.GetRotation() );
  507. break;
  508. case APERTURE::AM_ROUND_RECT: // Aperture macro for round rect pads
  509. {
  510. // The aperture macro needs coordinates of the centers of the 4 corners
  511. std::vector<VECTOR2I> corners;
  512. wxSize half_size( tool.m_Size.x/2-tool.m_Radius, tool.m_Size.y/2-tool.m_Radius );
  513. corners.emplace_back( -half_size.x, -half_size.y );
  514. corners.emplace_back( half_size.x, -half_size.y );
  515. corners.emplace_back( half_size.x, half_size.y );
  516. corners.emplace_back( -half_size.x, half_size.y );
  517. // Rotate the corner coordinates:
  518. for( int ii = 0; ii < 4; ii++ )
  519. RotatePoint( corners[ii], -tool.m_Rotation*10.0 );
  520. sprintf( cbuf, "%s,%#fX", APER_MACRO_ROUNDRECT_NAME,
  521. tool.m_Radius * fscale );
  522. buffer += cbuf;
  523. // Add each corner
  524. for( int ii = 0; ii < 4; ii++ )
  525. {
  526. sprintf( cbuf, "%#fX%#fX",
  527. corners[ii].x * fscale, corners[ii].y * fscale );
  528. buffer += cbuf;
  529. }
  530. sprintf( cbuf, "0*%%\n" );
  531. }
  532. break;
  533. case APERTURE::AM_ROT_RECT: // Aperture macro for rotated rect pads
  534. sprintf( cbuf, "%s,%#fX%#fX%#f*%%\n", APER_MACRO_ROT_RECT_NAME,
  535. tool.m_Size.x * fscale, tool.m_Size.y * fscale,
  536. tool.m_Rotation );
  537. break;
  538. case APERTURE::APER_MACRO_OUTLINE4P: // Aperture macro for trapezoid pads
  539. case APERTURE::APER_MACRO_OUTLINE5P: // Aperture macro for chamfered rect pads
  540. case APERTURE::APER_MACRO_OUTLINE6P: // Aperture macro for chamfered rect pads
  541. case APERTURE::APER_MACRO_OUTLINE7P: // Aperture macro for chamfered rect pads
  542. case APERTURE::APER_MACRO_OUTLINE8P: // Aperture macro for chamfered rect pads
  543. switch( tool.m_Type )
  544. {
  545. case APERTURE::APER_MACRO_OUTLINE4P:
  546. sprintf( cbuf, "%s,", APER_MACRO_OUTLINE4P_NAME ); break;
  547. case APERTURE::APER_MACRO_OUTLINE5P:
  548. sprintf( cbuf, "%s,", APER_MACRO_OUTLINE5P_NAME ); break;
  549. case APERTURE::APER_MACRO_OUTLINE6P:
  550. sprintf( cbuf, "%s,", APER_MACRO_OUTLINE6P_NAME ); break;
  551. case APERTURE::APER_MACRO_OUTLINE7P:
  552. sprintf( cbuf, "%s,", APER_MACRO_OUTLINE7P_NAME ); break;
  553. case APERTURE::APER_MACRO_OUTLINE8P:
  554. sprintf( cbuf, "%s,", APER_MACRO_OUTLINE8P_NAME ); break;
  555. default:
  556. break;
  557. }
  558. buffer += cbuf;
  559. // Output all corners (should be 4 to 8 corners)
  560. // Remember: the Y coordinate must be negated, due to the fact in Pcbnew
  561. // the Y axis is from top to bottom
  562. for( size_t ii = 0; ii < tool.m_Corners.size(); ii++ )
  563. {
  564. sprintf( cbuf, "%#fX%#fX",
  565. tool.m_Corners[ii].x * fscale, -tool.m_Corners[ii].y * fscale );
  566. buffer += cbuf;
  567. }
  568. // close outline and output rotation
  569. sprintf( cbuf, "%#f*%%\n", tool.m_Rotation );
  570. break;
  571. case APERTURE::AM_ROTATED_OVAL: // Aperture macro for rotated oval pads
  572. // (not rotated is a primitive)
  573. // m_Size.x = full lenght; m_Size.y = width, and the macro aperure expects
  574. // the position of ends
  575. {
  576. // the seg_len is the distance between the 2 circle centers
  577. int seg_len = tool.m_Size.x - tool.m_Size.y;
  578. // Center of the circle on the segment start point:
  579. VECTOR2I start( seg_len/2, 0 );
  580. // Center of the circle on the segment end point:
  581. VECTOR2I end( - seg_len/2, 0 );
  582. RotatePoint( start, tool.m_Rotation*10.0 );
  583. RotatePoint( end, tool.m_Rotation*10.0 );
  584. sprintf( cbuf, "%s,%#fX%#fX%#fX%#fX%#fX0*%%\n", APER_MACRO_SHAPE_OVAL_NAME,
  585. tool.m_Size.y * fscale, // width
  586. start.x * fscale, -start.y * fscale, // X,Y corner start pos
  587. end.x * fscale, -end.y * fscale ); // X,Y cornerend pos
  588. }
  589. break;
  590. case APERTURE::AM_FREE_POLYGON:
  591. {
  592. // Find the aperture macro name in the list of aperture macro
  593. // created on the fly for this polygon:
  594. int idx = m_am_freepoly_list.FindAm( tool.m_Corners );
  595. // Write DCODE id ( "%ADDxx" is already in buffer) and rotation
  596. // the full line is something like :%ADD12FreePoly1,45.000000*%
  597. sprintf( cbuf, "%s%d,%#f*%%\n", AM_FREEPOLY_BASENAME, idx, tool.m_Rotation );
  598. }
  599. break;
  600. }
  601. buffer += cbuf;
  602. fputs( buffer.c_str(), m_outputFile );
  603. m_apertureAttribute = attribute;
  604. // Currently reset the aperture attribute. Perhaps a better optimization
  605. // is to store the last attribute
  606. if( attribute )
  607. {
  608. if( m_useX2format )
  609. fputs( "%TD*%\n", m_outputFile );
  610. else
  611. fputs( "G04 #@! TD*\n", m_outputFile );
  612. m_apertureAttribute = 0;
  613. }
  614. }
  615. }
  616. void GERBER_PLOTTER::PenTo( const wxPoint& aPos, char plume )
  617. {
  618. wxASSERT( m_outputFile );
  619. DPOINT pos_dev = userToDeviceCoordinates( aPos );
  620. switch( plume )
  621. {
  622. case 'Z':
  623. break;
  624. case 'U':
  625. emitDcode( pos_dev, 2 );
  626. break;
  627. case 'D':
  628. emitDcode( pos_dev, 1 );
  629. }
  630. m_penState = plume;
  631. }
  632. void GERBER_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_TYPE fill, int width )
  633. {
  634. std::vector< wxPoint > cornerList;
  635. // Build corners list
  636. cornerList.push_back( p1 );
  637. wxPoint corner(p1.x, p2.y);
  638. cornerList.push_back( corner );
  639. cornerList.push_back( p2 );
  640. corner.x = p2.x;
  641. corner.y = p1.y;
  642. cornerList.push_back( corner );
  643. cornerList.push_back( p1 );
  644. PlotPoly( cornerList, fill, width );
  645. }
  646. void GERBER_PLOTTER::Circle( const wxPoint& aCenter, int aDiameter, FILL_TYPE aFill, int aWidth )
  647. {
  648. Arc( aCenter, 0, 3600, aDiameter / 2, aFill, aWidth );
  649. }
  650. void GERBER_PLOTTER::Arc( const wxPoint& aCenter, double aStAngle, double aEndAngle,
  651. int aRadius, FILL_TYPE aFill, int aWidth )
  652. {
  653. SetCurrentLineWidth( aWidth );
  654. // aFill is not used here.
  655. plotArc( aCenter, aStAngle, aEndAngle, aRadius, false );
  656. }
  657. void GERBER_PLOTTER::plotArc( const wxPoint& aCenter, double aStAngle, double aEndAngle,
  658. int aRadius, bool aPlotInRegion )
  659. {
  660. wxPoint start, end;
  661. start.x = aCenter.x + KiROUND( cosdecideg( aRadius, aStAngle ) );
  662. start.y = aCenter.y - KiROUND( sindecideg( aRadius, aStAngle ) );
  663. if( !aPlotInRegion )
  664. MoveTo( start );
  665. else
  666. LineTo( start );
  667. end.x = aCenter.x + KiROUND( cosdecideg( aRadius, aEndAngle ) );
  668. end.y = aCenter.y - KiROUND( sindecideg( aRadius, aEndAngle ) );
  669. DPOINT devEnd = userToDeviceCoordinates( end );
  670. DPOINT devCenter = userToDeviceCoordinates( aCenter ) - userToDeviceCoordinates( start );
  671. fprintf( m_outputFile, "G75*\n" ); // Multiquadrant (360 degrees) mode
  672. if( aStAngle < aEndAngle )
  673. fprintf( m_outputFile, "G03*\n" ); // Active circular interpolation, CCW
  674. else
  675. fprintf( m_outputFile, "G02*\n" ); // Active circular interpolation, CW
  676. fprintf( m_outputFile, "X%dY%dI%dJ%dD01*\n",
  677. KiROUND( devEnd.x ), KiROUND( devEnd.y ),
  678. KiROUND( devCenter.x ), KiROUND( devCenter.y ) );
  679. fprintf( m_outputFile, "G01*\n" ); // Back to linear interpol (perhaps useless here).
  680. }
  681. void GERBER_PLOTTER::PlotGerberRegion( const std::vector< wxPoint >& aCornerList,
  682. void * aData )
  683. {
  684. if( aCornerList.size() <= 2 )
  685. return;
  686. GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
  687. bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
  688. if( gbr_metadata )
  689. {
  690. std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
  691. if( !attrib.empty() )
  692. {
  693. fputs( attrib.c_str(), m_outputFile );
  694. clearTA_AperFunction = true;
  695. }
  696. }
  697. PlotPoly( aCornerList, FILL_TYPE::FILLED_SHAPE, 0, gbr_metadata );
  698. // Clear the TA attribute, to avoid the next item to inherit it:
  699. if( clearTA_AperFunction )
  700. {
  701. if( m_useX2format )
  702. {
  703. fputs( "%TD.AperFunction*%\n", m_outputFile );
  704. }
  705. else
  706. {
  707. fputs( "G04 #@! TD.AperFunction*\n", m_outputFile );
  708. }
  709. }
  710. }
  711. void GERBER_PLOTTER::PlotPoly( const std::vector< wxPoint >& aCornerList,
  712. FILL_TYPE aFill, int aWidth, void * aData )
  713. {
  714. if( aCornerList.size() <= 1 )
  715. return;
  716. // Gerber format does not know filled polygons with thick outline
  717. // Therefore, to plot a filled polygon with outline having a thickness,
  718. // one should plot outline as thick segments
  719. GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
  720. if( gbr_metadata )
  721. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  722. if( aFill != FILL_TYPE::NO_FILL )
  723. {
  724. fputs( "G36*\n", m_outputFile );
  725. MoveTo( aCornerList[0] );
  726. fputs( "G01*\n", m_outputFile ); // Set linear interpolation.
  727. for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
  728. LineTo( aCornerList[ii] );
  729. // If the polygon is not closed, close it:
  730. if( aCornerList[0] != aCornerList[aCornerList.size()-1] )
  731. FinishTo( aCornerList[0] );
  732. fputs( "G37*\n", m_outputFile );
  733. }
  734. if( aWidth > 0 ) // Draw the polyline/polygon outline
  735. {
  736. SetCurrentLineWidth( aWidth, gbr_metadata );
  737. MoveTo( aCornerList[0] );
  738. for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
  739. LineTo( aCornerList[ii] );
  740. // Ensure the thick outline is closed for filled polygons
  741. // (if not filled, could be only a polyline)
  742. if( aFill != FILL_TYPE::NO_FILL &&( aCornerList[aCornerList.size() - 1] != aCornerList[0] ) )
  743. LineTo( aCornerList[0] );
  744. PenFinish();
  745. }
  746. }
  747. void GERBER_PLOTTER::ThickSegment( const wxPoint& start, const wxPoint& end, int width,
  748. OUTLINE_MODE tracemode, void* aData )
  749. {
  750. if( tracemode == FILLED )
  751. {
  752. GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
  753. SetCurrentLineWidth( width, gbr_metadata );
  754. if( gbr_metadata )
  755. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  756. MoveTo( start );
  757. FinishTo( end );
  758. }
  759. else
  760. {
  761. SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH );
  762. segmentAsOval( start, end, width, tracemode );
  763. }
  764. }
  765. void GERBER_PLOTTER::ThickArc( const wxPoint& centre, double StAngle, double EndAngle,
  766. int radius, int width, OUTLINE_MODE tracemode, void* aData )
  767. {
  768. GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
  769. SetCurrentLineWidth( width, gbr_metadata );
  770. if( gbr_metadata )
  771. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  772. if( tracemode == FILLED )
  773. Arc( centre, StAngle, EndAngle, radius, FILL_TYPE::NO_FILL, DO_NOT_SET_LINE_WIDTH );
  774. else
  775. {
  776. SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH );
  777. Arc( centre, StAngle, EndAngle,
  778. radius - ( width - m_currentPenWidth ) / 2, FILL_TYPE::NO_FILL,
  779. DO_NOT_SET_LINE_WIDTH );
  780. Arc( centre, StAngle, EndAngle, radius + ( width - m_currentPenWidth ) / 2, FILL_TYPE::NO_FILL,
  781. DO_NOT_SET_LINE_WIDTH );
  782. }
  783. }
  784. void GERBER_PLOTTER::ThickRect( const wxPoint& p1, const wxPoint& p2, int width,
  785. OUTLINE_MODE tracemode, void* aData )
  786. {
  787. GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
  788. SetCurrentLineWidth( width, gbr_metadata );
  789. if( gbr_metadata )
  790. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  791. if( tracemode == FILLED )
  792. Rect( p1, p2, FILL_TYPE::NO_FILL, DO_NOT_SET_LINE_WIDTH );
  793. else
  794. {
  795. SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH );
  796. wxPoint offsetp1( p1.x - (width - m_currentPenWidth) / 2,
  797. p1.y - (width - m_currentPenWidth) / 2 );
  798. wxPoint offsetp2( p2.x + (width - m_currentPenWidth) / 2,
  799. p2.y + (width - m_currentPenWidth) / 2 );
  800. Rect( offsetp1, offsetp2, FILL_TYPE::NO_FILL, -1 );
  801. offsetp1.x += (width - m_currentPenWidth);
  802. offsetp1.y += (width - m_currentPenWidth);
  803. offsetp2.x -= (width - m_currentPenWidth);
  804. offsetp2.y -= (width - m_currentPenWidth);
  805. Rect( offsetp1, offsetp2, FILL_TYPE::NO_FILL, DO_NOT_SET_LINE_WIDTH );
  806. }
  807. }
  808. void GERBER_PLOTTER::ThickCircle( const wxPoint& pos, int diametre, int width,
  809. OUTLINE_MODE tracemode, void* aData )
  810. {
  811. GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
  812. SetCurrentLineWidth( width, gbr_metadata );
  813. if( gbr_metadata )
  814. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  815. if( tracemode == FILLED )
  816. Circle( pos, diametre, FILL_TYPE::NO_FILL, DO_NOT_SET_LINE_WIDTH );
  817. else
  818. {
  819. SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, gbr_metadata );
  820. Circle( pos, diametre - (width - m_currentPenWidth),
  821. FILL_TYPE::NO_FILL, DO_NOT_SET_LINE_WIDTH );
  822. Circle( pos, diametre + (width - m_currentPenWidth),
  823. FILL_TYPE::NO_FILL, DO_NOT_SET_LINE_WIDTH );
  824. }
  825. }
  826. void GERBER_PLOTTER::FilledCircle( const wxPoint& pos, int diametre,
  827. OUTLINE_MODE tracemode, void* aData )
  828. {
  829. // A filled circle is a graphic item, not a pad.
  830. // So it is drawn, not flashed.
  831. GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
  832. if( gbr_metadata )
  833. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  834. if( tracemode == FILLED )
  835. {
  836. // Draw a circle of diameter = diametre/2 with a line thickness = radius,
  837. // To create a filled circle
  838. SetCurrentLineWidth( diametre/2, gbr_metadata );
  839. Circle( pos, diametre/2, FILL_TYPE::NO_FILL, DO_NOT_SET_LINE_WIDTH );
  840. }
  841. else
  842. {
  843. SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, gbr_metadata );
  844. Circle( pos, diametre, FILL_TYPE::NO_FILL, DO_NOT_SET_LINE_WIDTH );
  845. }
  846. }
  847. void GERBER_PLOTTER::FlashPadCircle( const wxPoint& pos, int diametre, OUTLINE_MODE trace_mode, void* aData )
  848. {
  849. wxSize size( diametre, diametre );
  850. GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
  851. if( trace_mode == SKETCH )
  852. {
  853. if( gbr_metadata )
  854. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  855. SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH );
  856. Circle( pos, diametre - m_currentPenWidth, FILL_TYPE::NO_FILL, DO_NOT_SET_LINE_WIDTH );
  857. }
  858. else
  859. {
  860. DPOINT pos_dev = userToDeviceCoordinates( pos );
  861. int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
  862. selectAperture( size, 0, 0.0, APERTURE::AT_CIRCLE, aperture_attrib );
  863. if( gbr_metadata )
  864. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  865. emitDcode( pos_dev, 3 );
  866. }
  867. }
  868. void GERBER_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double orient,
  869. OUTLINE_MODE trace_mode, void* aData )
  870. {
  871. wxASSERT( m_outputFile );
  872. wxSize size( aSize );
  873. GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
  874. // Flash a vertical or horizontal shape (this is a basic aperture).
  875. if( ( orient == 0 || orient == 900 || orient == 1800 || orient == 2700 )
  876. && trace_mode == FILLED )
  877. {
  878. if( orient == 900 || orient == 2700 ) /* orientation turned 90 deg. */
  879. std::swap( size.x, size.y );
  880. DPOINT pos_dev = userToDeviceCoordinates( pos );
  881. int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
  882. selectAperture( size, 0, 0.0, APERTURE::AT_OVAL, aperture_attrib );
  883. if( gbr_metadata )
  884. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  885. emitDcode( pos_dev, 3 );
  886. }
  887. else // Plot pad as region.
  888. // Only regions and flashed items accept a object attribute TO.P for the pin name
  889. {
  890. if( trace_mode == FILLED )
  891. {
  892. #ifdef GBR_USE_MACROS_FOR_ROTATED_OVAL
  893. if( !m_gerberDisableApertMacros )
  894. #endif
  895. {
  896. m_hasApertureRotOval = true;
  897. // We are using a aperture macro that expect size.y < size.x
  898. // i.e draw a horizontal line for rotation = 0.0
  899. // size.x = length, size.y = width
  900. if( size.x < size.y )
  901. {
  902. std::swap( size.x, size.y );
  903. orient += 900;
  904. if( orient > 1800 )
  905. orient -= 1800;
  906. }
  907. DPOINT pos_dev = userToDeviceCoordinates( pos );
  908. int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
  909. selectAperture( size, 0, orient/10.0, APERTURE::AM_ROTATED_OVAL, aperture_attrib );
  910. if( gbr_metadata )
  911. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  912. emitDcode( pos_dev, 3 );
  913. return;
  914. }
  915. // Draw the oval as round rect pad with a radius = 50% min size)
  916. // In gerber file, it will be drawn as a region with arcs, and can be
  917. // detected as pads (similar to a flashed pad)
  918. FlashPadRoundRect( pos, aSize, std::min( aSize.x, aSize.y ) /2,
  919. orient, FILLED, aData );
  920. }
  921. else // Non filled shape: plot outlines:
  922. {
  923. if( size.x > size.y )
  924. {
  925. std::swap( size.x, size.y );
  926. if( orient < 2700 )
  927. orient += 900;
  928. else
  929. orient -= 2700;
  930. }
  931. sketchOval( pos, size, orient, -1 );
  932. }
  933. }
  934. }
  935. void GERBER_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& aSize,
  936. double orient, OUTLINE_MODE trace_mode, void* aData )
  937. {
  938. wxASSERT( m_outputFile );
  939. wxSize size( aSize );
  940. GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
  941. // Plot as an aperture flash
  942. switch( int( orient ) )
  943. {
  944. case 900:
  945. case 2700: // rotation of 90 degrees or 270 swaps sizes
  946. std::swap( size.x, size.y );
  947. KI_FALLTHROUGH;
  948. case 0:
  949. case 1800:
  950. if( trace_mode == SKETCH )
  951. {
  952. if( gbr_metadata )
  953. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  954. SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH );
  955. Rect( wxPoint( pos.x - (size.x - GetCurrentLineWidth()) / 2,
  956. pos.y - (size.y - GetCurrentLineWidth()) / 2 ),
  957. wxPoint( pos.x + (size.x - GetCurrentLineWidth()) / 2,
  958. pos.y + (size.y - GetCurrentLineWidth()) / 2 ),
  959. FILL_TYPE::NO_FILL, GetCurrentLineWidth() );
  960. }
  961. else
  962. {
  963. DPOINT pos_dev = userToDeviceCoordinates( pos );
  964. int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
  965. selectAperture( size, 0, 0.0, APERTURE::AT_RECT, aperture_attrib );
  966. if( gbr_metadata )
  967. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  968. emitDcode( pos_dev, 3 );
  969. }
  970. break;
  971. default:
  972. #ifdef GBR_USE_MACROS_FOR_ROTATED_RECT
  973. if( trace_mode != SKETCH && !m_gerberDisableApertMacros )
  974. {
  975. m_hasApertureRotRect = true;
  976. DPOINT pos_dev = userToDeviceCoordinates( pos );
  977. int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
  978. selectAperture( size, 0, orient/10.0, APERTURE::AM_ROT_RECT, aperture_attrib );
  979. if( gbr_metadata )
  980. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  981. emitDcode( pos_dev, 3 );
  982. break;
  983. }
  984. #endif
  985. {
  986. // plot pad shape as Gerber region
  987. wxPoint coord[4];
  988. // coord[0] is assumed the lower left
  989. // coord[1] is assumed the upper left
  990. // coord[2] is assumed the upper right
  991. // coord[3] is assumed the lower right
  992. coord[0].x = -size.x/2; // lower left
  993. coord[0].y = size.y/2;
  994. coord[1].x = -size.x/2; // upper left
  995. coord[1].y = -size.y/2;
  996. coord[2].x = size.x/2; // upper right
  997. coord[2].y = -size.y/2;
  998. coord[3].x = size.x/2; // lower right
  999. coord[3].y = size.y/2;
  1000. FlashPadTrapez( pos, coord, orient, trace_mode, aData );
  1001. }
  1002. break;
  1003. }
  1004. }
  1005. void GERBER_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSize,
  1006. int aCornerRadius, double aOrient,
  1007. OUTLINE_MODE aTraceMode, void* aData )
  1008. {
  1009. GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
  1010. if( aTraceMode != FILLED )
  1011. {
  1012. SHAPE_POLY_SET outline;
  1013. TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient,
  1014. aCornerRadius, 0.0, 0, GetPlotterArcHighDef(), ERROR_INSIDE );
  1015. SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata );
  1016. outline.Inflate( -GetCurrentLineWidth()/2, 16 );
  1017. std::vector< wxPoint > cornerList;
  1018. // TransformRoundRectToPolygon creates only one convex polygon
  1019. SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
  1020. cornerList.reserve( poly.PointCount() + 1 );
  1021. for( int ii = 0; ii < poly.PointCount(); ++ii )
  1022. cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
  1023. // Close polygon
  1024. cornerList.push_back( cornerList[0] );
  1025. // plot outlines
  1026. PlotPoly( cornerList, FILL_TYPE::NO_FILL, GetCurrentLineWidth(), gbr_metadata );
  1027. }
  1028. else
  1029. {
  1030. #ifdef GBR_USE_MACROS_FOR_ROUNDRECT
  1031. if( !m_gerberDisableApertMacros )
  1032. #endif
  1033. {
  1034. m_hasApertureRoundRect = true;
  1035. DPOINT pos_dev = userToDeviceCoordinates( aPadPos );
  1036. int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
  1037. selectAperture( aSize, aCornerRadius, aOrient/10.0,
  1038. APERTURE::AM_ROUND_RECT, aperture_attrib );
  1039. if( gbr_metadata )
  1040. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  1041. emitDcode( pos_dev, 3 );
  1042. return;
  1043. }
  1044. // A Pad RoundRect is plotted as a Gerber region.
  1045. // Initialize region metadata:
  1046. bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
  1047. if( gbr_metadata )
  1048. {
  1049. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  1050. std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
  1051. if( !attrib.empty() )
  1052. {
  1053. fputs( attrib.c_str(), m_outputFile );
  1054. clearTA_AperFunction = true;
  1055. }
  1056. }
  1057. // Plot the region using arcs in corners.
  1058. plotRoundRectAsRegion( aPadPos, aSize, aCornerRadius, aOrient );
  1059. // Clear the TA attribute, to avoid the next item to inherit it:
  1060. if( clearTA_AperFunction )
  1061. {
  1062. if( m_useX2format )
  1063. fputs( "%TD.AperFunction*%\n", m_outputFile );
  1064. else
  1065. fputs( "G04 #@! TD.AperFunction*\n", m_outputFile );
  1066. }
  1067. }
  1068. }
  1069. void GERBER_PLOTTER::plotRoundRectAsRegion( const wxPoint& aRectCenter, const wxSize& aSize,
  1070. int aCornerRadius, double aOrient )
  1071. {
  1072. // The region outline is generated by 4 sides and 4 90 deg arcs
  1073. // 1 --- 2
  1074. // | c |
  1075. // 4 --- 3
  1076. // Note also in user coordinates the Y axis is from top to bottom
  1077. // for historical reasons.
  1078. // A helper structure to handle outlines coordinates (segments and arcs)
  1079. // in user coordinates
  1080. struct RR_EDGE
  1081. {
  1082. wxPoint m_start;
  1083. wxPoint m_end;
  1084. wxPoint m_center;
  1085. // in decidegrees: angle start. angle end = m_arc_angle_start+arc_angle
  1086. double m_arc_angle_start;
  1087. };
  1088. const double arc_angle = -900.0; // in decidegrees
  1089. int hsizeX = aSize.x/2;
  1090. int hsizeY = aSize.y/2;
  1091. RR_EDGE curr_edge;
  1092. std::vector<RR_EDGE> rr_outline;
  1093. // Build outline coordinates, relative to rectangle center, rotation 0:
  1094. // Top left corner 1 (and 4 to 1 left vertical side @ x=-hsizeX)
  1095. curr_edge.m_start.x = -hsizeX;
  1096. curr_edge.m_start.y = hsizeY - aCornerRadius;
  1097. curr_edge.m_end.x = curr_edge.m_start.x;
  1098. curr_edge.m_end.y = -hsizeY + aCornerRadius;
  1099. curr_edge.m_center.x = -hsizeX + aCornerRadius;
  1100. curr_edge.m_center.y = curr_edge.m_end.y;
  1101. curr_edge.m_arc_angle_start = aOrient + 1800.0; // En decidegree
  1102. rr_outline.push_back( curr_edge );
  1103. // Top right corner 2 (and 1 to 2 top horizontal side @ y=-hsizeY)
  1104. curr_edge.m_start.x = -hsizeX + aCornerRadius;
  1105. curr_edge.m_start.y = -hsizeY;
  1106. curr_edge.m_end.x = hsizeX - aCornerRadius;
  1107. curr_edge.m_end.y = curr_edge.m_start.y;
  1108. curr_edge.m_center.x = curr_edge.m_end.x;
  1109. curr_edge.m_center.y = -hsizeY + aCornerRadius;
  1110. curr_edge.m_arc_angle_start = aOrient + 900.0; // En decidegree
  1111. rr_outline.push_back( curr_edge );
  1112. // bottom right corner 3 (and 2 to 3 right vertical side @ x=hsizeX)
  1113. curr_edge.m_start.x = hsizeX;
  1114. curr_edge.m_start.y = -hsizeY + aCornerRadius;
  1115. curr_edge.m_end.x = curr_edge.m_start.x;
  1116. curr_edge.m_end.y = hsizeY - aCornerRadius;
  1117. curr_edge.m_center.x = hsizeX - aCornerRadius;
  1118. curr_edge.m_center.y = curr_edge.m_end.y;
  1119. curr_edge.m_arc_angle_start = aOrient + 0.0; // En decidegree
  1120. rr_outline.push_back( curr_edge );
  1121. // bottom left corner 4 (and 3 to 4 bottom horizontal side @ y=hsizeY)
  1122. curr_edge.m_start.x = hsizeX - aCornerRadius;
  1123. curr_edge.m_start.y = hsizeY;
  1124. curr_edge.m_end.x = -hsizeX + aCornerRadius;
  1125. curr_edge.m_end.y = curr_edge.m_start.y;
  1126. curr_edge.m_center.x = curr_edge.m_end.x;
  1127. curr_edge.m_center.y = hsizeY - aCornerRadius;
  1128. curr_edge.m_arc_angle_start = aOrient - 900.0; // En decidegree
  1129. rr_outline.push_back( curr_edge );
  1130. // Move relative coordinates to the actual location and rotation:
  1131. wxPoint arc_last_center;
  1132. int arc_last_angle = curr_edge.m_arc_angle_start+arc_angle;
  1133. for( RR_EDGE& rr_edge: rr_outline )
  1134. {
  1135. RotatePoint( &rr_edge.m_start, aOrient );
  1136. RotatePoint( &rr_edge.m_end, aOrient );
  1137. RotatePoint( &rr_edge.m_center, aOrient );
  1138. rr_edge.m_start += aRectCenter;
  1139. rr_edge.m_end += aRectCenter;
  1140. rr_edge.m_center += aRectCenter;
  1141. arc_last_center = rr_edge.m_center;
  1142. }
  1143. // Ensure the region is a closed polygon, i.e. the end point of last segment
  1144. // (end of arc) is the same as the first point. Rounding issues can create a
  1145. // small difference, mainly for rotated pads.
  1146. // calculate last point (end of last arc):
  1147. wxPoint last_pt;
  1148. last_pt.x = arc_last_center.x + KiROUND( cosdecideg( aCornerRadius, arc_last_angle ) );
  1149. last_pt.y = arc_last_center.y - KiROUND( sindecideg( aCornerRadius, arc_last_angle ) );
  1150. wxPoint first_pt = rr_outline[0].m_start;
  1151. #if 0 // For test only:
  1152. if( last_pt != first_pt )
  1153. wxLogMessage( "first pt %d %d last pt %d %d",
  1154. first_pt.x, first_pt.y, last_pt.x, last_pt.y );
  1155. #endif
  1156. fputs( "G36*\n", m_outputFile ); // Start region
  1157. fputs( "G01*\n", m_outputFile ); // Set linear interpolation.
  1158. first_pt = last_pt;
  1159. MoveTo( first_pt ); // Start point of region, must be same as end point
  1160. for( RR_EDGE& rr_edge: rr_outline )
  1161. {
  1162. if( aCornerRadius ) // Guard: ensure we do not create arcs with radius = 0
  1163. {
  1164. // LineTo( rr_edge.m_end ); // made in plotArc()
  1165. plotArc( rr_edge.m_center,
  1166. rr_edge.m_arc_angle_start, rr_edge.m_arc_angle_start+arc_angle,
  1167. aCornerRadius, true );
  1168. }
  1169. else
  1170. LineTo( rr_edge.m_end );
  1171. }
  1172. fputs( "G37*\n", m_outputFile ); // Close region
  1173. }
  1174. void GERBER_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize,
  1175. double aOrient, SHAPE_POLY_SET* aPolygons,
  1176. OUTLINE_MODE aTraceMode, void* aData )
  1177. {
  1178. // A Pad custom is plotted as polygon (a region in Gerber language).
  1179. GBR_METADATA gbr_metadata;
  1180. if( aData )
  1181. gbr_metadata = *static_cast<GBR_METADATA*>( aData );
  1182. SHAPE_POLY_SET polyshape = *aPolygons;
  1183. if( aTraceMode != FILLED )
  1184. {
  1185. SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata );
  1186. polyshape.Inflate( -GetCurrentLineWidth()/2, 16 );
  1187. }
  1188. std::vector< wxPoint > cornerList;
  1189. for( int cnt = 0; cnt < polyshape.OutlineCount(); ++cnt )
  1190. {
  1191. SHAPE_LINE_CHAIN& poly = polyshape.Outline( cnt );
  1192. cornerList.clear();
  1193. for( int ii = 0; ii < poly.PointCount(); ++ii )
  1194. cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
  1195. // Close polygon
  1196. cornerList.push_back( cornerList[0] );
  1197. if( aTraceMode == SKETCH )
  1198. PlotPoly( cornerList, FILL_TYPE::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
  1199. else
  1200. {
  1201. #ifdef GBR_USE_MACROS_FOR_CUSTOM_PAD
  1202. if( m_gerberDisableApertMacros
  1203. || cornerList.size() > GBR_MACRO_FOR_CUSTOM_PAD_MAX_CORNER_COUNT )
  1204. PlotGerberRegion( cornerList, &gbr_metadata );
  1205. else
  1206. {
  1207. // An AM will be created. the shape must be in position 0,0 and orientation 0
  1208. // to be able to reuse the same AM for pads having the same shape
  1209. for( size_t ii = 0; ii < cornerList.size(); ii++ )
  1210. {
  1211. cornerList[ii] -= aPadPos;
  1212. RotatePoint( &cornerList[ii], -aOrient );
  1213. }
  1214. DPOINT pos_dev = userToDeviceCoordinates( aPadPos );
  1215. selectAperture( cornerList, aOrient/10.0,
  1216. APERTURE::AM_FREE_POLYGON, gbr_metadata.GetApertureAttrib() );
  1217. formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
  1218. emitDcode( pos_dev, 3 );
  1219. }
  1220. #else
  1221. PlotGerberRegion( cornerList, &gbr_metadata );
  1222. #endif
  1223. }
  1224. }
  1225. }
  1226. void GERBER_PLOTTER::FlashPadChamferRoundRect( const wxPoint& aShapePos, const wxSize& aPadSize,
  1227. int aCornerRadius, double aChamferRatio,
  1228. int aChamferPositions,
  1229. double aPadOrient, OUTLINE_MODE aPlotMode, void* aData )
  1230. {
  1231. GBR_METADATA gbr_metadata;
  1232. if( aData )
  1233. gbr_metadata = *static_cast<GBR_METADATA*>( aData );
  1234. DPOINT pos_dev = userToDeviceCoordinates( aShapePos );
  1235. SHAPE_POLY_SET outline;
  1236. // polygon corners list
  1237. std::vector<wxPoint> cornerList;
  1238. bool hasRoundedCorner = aCornerRadius != 0 && aChamferPositions != 15;
  1239. #ifdef GBR_USE_MACROS_FOR_CHAMFERED_RECT
  1240. // Sketch mode or round rect shape or Apert Macros disabled
  1241. if( aPlotMode != FILLED || hasRoundedCorner || m_gerberDisableApertMacros )
  1242. #endif
  1243. {
  1244. TransformRoundChamferedRectToPolygon( outline, aShapePos, aPadSize, aPadOrient,
  1245. aCornerRadius, aChamferRatio, aChamferPositions,
  1246. GetPlotterArcHighDef(), ERROR_INSIDE );
  1247. // Build the corner list
  1248. const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
  1249. for( int ii = 0; ii < corners.PointCount(); ii++ )
  1250. cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
  1251. // Close the polygon
  1252. cornerList.push_back( cornerList[0] );
  1253. if( aPlotMode == SKETCH )
  1254. PlotPoly( cornerList, FILL_TYPE::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
  1255. else
  1256. {
  1257. #ifdef GBR_USE_MACROS_FOR_CHAMFERED_ROUND_RECT
  1258. if( m_gerberDisableApertMacros )
  1259. PlotGerberRegion( cornerList, &gbr_metadata );
  1260. else
  1261. {
  1262. // An AM will be created. the shape must be in position 0,0 and orientation 0
  1263. // to be able to reuse the same AM for pads having the same shape
  1264. for( size_t ii = 0; ii < cornerList.size(); ii++ )
  1265. {
  1266. cornerList[ii] -= aShapePos;
  1267. RotatePoint( &cornerList[ii], -aPadOrient );
  1268. }
  1269. selectAperture( cornerList, aPadOrient/10.0,
  1270. APERTURE::AM_FREE_POLYGON, gbr_metadata.GetApertureAttrib() );
  1271. formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
  1272. emitDcode( pos_dev, 3 );
  1273. }
  1274. #else
  1275. PlotGerberRegion( cornerList, &gbr_metadata );
  1276. #endif
  1277. }
  1278. return;
  1279. }
  1280. // Build the chamfered polygon (4 to 8 corners )
  1281. TransformRoundChamferedRectToPolygon( outline, wxPoint( 0, 0 ), aPadSize, 0.0, 0,
  1282. aChamferRatio, aChamferPositions,
  1283. GetPlotterArcHighDef(), ERROR_INSIDE );
  1284. // Build the corner list
  1285. const SHAPE_LINE_CHAIN& corners = outline.Outline(0);
  1286. // Generate the polygon (4 to 8 corners )
  1287. for( int ii = 0; ii < corners.PointCount(); ii++ )
  1288. cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y );
  1289. switch( cornerList.size() )
  1290. {
  1291. case 4:
  1292. m_hasApertureOutline4P = true;
  1293. selectAperture( cornerList, aPadOrient/10.0,
  1294. APERTURE::APER_MACRO_OUTLINE4P, gbr_metadata.GetApertureAttrib() );
  1295. break;
  1296. case 5:
  1297. m_hasApertureChamferedRect = true;
  1298. selectAperture( cornerList, aPadOrient/10.0,
  1299. APERTURE::APER_MACRO_OUTLINE5P, gbr_metadata.GetApertureAttrib() );
  1300. break;
  1301. case 6:
  1302. m_hasApertureChamferedRect = true;
  1303. selectAperture( cornerList, aPadOrient/10.0,
  1304. APERTURE::APER_MACRO_OUTLINE6P, gbr_metadata.GetApertureAttrib() );
  1305. break;
  1306. case 7:
  1307. m_hasApertureChamferedRect = true;
  1308. selectAperture( cornerList, aPadOrient/10.0,
  1309. APERTURE::APER_MACRO_OUTLINE7P, gbr_metadata.GetApertureAttrib() );
  1310. break;
  1311. case 8:
  1312. m_hasApertureChamferedRect = true;
  1313. selectAperture( cornerList, aPadOrient/10.0,
  1314. APERTURE::APER_MACRO_OUTLINE8P, gbr_metadata.GetApertureAttrib() );
  1315. break;
  1316. default:
  1317. wxLogMessage( "FlashPadChamferRoundRect(): Unexpected number of corners (%d)",
  1318. (int)cornerList.size() );
  1319. break;
  1320. }
  1321. formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
  1322. emitDcode( pos_dev, 3 );
  1323. }
  1324. void GERBER_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCorners,
  1325. double aPadOrient, OUTLINE_MODE aTrace_Mode, void* aData )
  1326. {
  1327. // polygon corners list
  1328. std::vector<wxPoint> cornerList = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
  1329. // Draw the polygon and fill the interior as required
  1330. for( unsigned ii = 0; ii < 4; ii++ )
  1331. {
  1332. RotatePoint( &cornerList[ii], aPadOrient );
  1333. cornerList[ii] += aPadPos;
  1334. }
  1335. // Close the polygon
  1336. cornerList.push_back( cornerList[0] );
  1337. GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
  1338. GBR_METADATA metadata;
  1339. if( gbr_metadata )
  1340. metadata = *gbr_metadata;
  1341. if( aTrace_Mode == SKETCH )
  1342. {
  1343. PlotPoly( cornerList, FILL_TYPE::NO_FILL, GetCurrentLineWidth(), &metadata );
  1344. return;
  1345. }
  1346. // Plot a filled polygon:
  1347. #ifdef GBR_USE_MACROS_FOR_TRAPEZOID
  1348. if( !m_gerberDisableApertMacros )
  1349. #endif
  1350. {
  1351. m_hasApertureOutline4P = true;
  1352. DPOINT pos_dev = userToDeviceCoordinates( aPadPos );
  1353. // polygon corners list
  1354. std::vector<wxPoint> corners = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
  1355. int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
  1356. selectAperture( corners, aPadOrient/10.0, APERTURE::APER_MACRO_OUTLINE4P, aperture_attrib );
  1357. if( gbr_metadata )
  1358. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  1359. emitDcode( pos_dev, 3 );
  1360. return;
  1361. }
  1362. PlotGerberRegion( cornerList, &metadata );
  1363. }
  1364. void GERBER_PLOTTER::FlashRegularPolygon( const wxPoint& aShapePos,
  1365. int aDiameter, int aCornerCount,
  1366. double aOrient, OUTLINE_MODE aTraceMode,
  1367. void* aData )
  1368. {
  1369. GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
  1370. GBR_METADATA metadata;
  1371. if( gbr_metadata )
  1372. metadata = *gbr_metadata;
  1373. if( aTraceMode == SKETCH )
  1374. {
  1375. // Build the polygon:
  1376. std::vector< wxPoint > cornerList;
  1377. double angle_delta = 3600.0 / aCornerCount; // in 0.1 degree
  1378. for( int ii = 0; ii < aCornerCount; ii++ )
  1379. {
  1380. double rot = aOrient + (angle_delta*ii);
  1381. wxPoint vertice( aDiameter/2, 0 );
  1382. RotatePoint( &vertice, rot );
  1383. vertice += aShapePos;
  1384. cornerList.push_back( vertice );
  1385. }
  1386. cornerList.push_back( cornerList[0] ); // Close the shape
  1387. PlotPoly( cornerList, FILL_TYPE::NO_FILL, GetCurrentLineWidth(), &gbr_metadata );
  1388. }
  1389. else
  1390. {
  1391. DPOINT pos_dev = userToDeviceCoordinates( aShapePos );
  1392. int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
  1393. APERTURE::APERTURE_TYPE apert_type =
  1394. (APERTURE::APERTURE_TYPE)(APERTURE::AT_REGULAR_POLY3 + aCornerCount - 3);
  1395. selectAperture( aDiameter, aOrient, apert_type, aperture_attrib );
  1396. if( gbr_metadata )
  1397. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  1398. emitDcode( pos_dev, 3 );
  1399. }
  1400. }
  1401. void GERBER_PLOTTER::Text( const wxPoint& aPos, const COLOR4D aColor,
  1402. const wxString& aText, double aOrient, const wxSize& aSize,
  1403. enum EDA_TEXT_HJUSTIFY_T aH_justify,
  1404. enum EDA_TEXT_VJUSTIFY_T aV_justify, int aWidth, bool aItalic,
  1405. bool aBold, bool aMultilineAllowed, void* aData )
  1406. {
  1407. GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
  1408. if( gbr_metadata )
  1409. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  1410. PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth, aItalic,
  1411. aBold, aMultilineAllowed, aData );
  1412. }
  1413. void GERBER_PLOTTER::SetLayerPolarity( bool aPositive )
  1414. {
  1415. if( aPositive )
  1416. fprintf( m_outputFile, "%%LPD*%%\n" );
  1417. else
  1418. fprintf( m_outputFile, "%%LPC*%%\n" );
  1419. }
  1420. bool APER_MACRO_FREEPOLY::IsSamePoly( const std::vector<wxPoint>& aPolygon ) const
  1421. {
  1422. return polyCompare( m_Corners, aPolygon );
  1423. }
  1424. void APER_MACRO_FREEPOLY::Format( FILE * aOutput, double aIu2GbrMacroUnit )
  1425. {
  1426. // Write aperture header
  1427. fprintf( aOutput, "%%AM%s%d*\n", AM_FREEPOLY_BASENAME, m_Id );
  1428. fprintf( aOutput, "4,1,%d,", (int)m_Corners.size() );
  1429. // Insert a newline after curr_line_count_max coordinates.
  1430. int curr_line_corner_count = 0;
  1431. const int curr_line_count_max = 20; // <= 0 to disable newlines
  1432. for( size_t ii = 0; ii <= m_Corners.size(); ii++ )
  1433. {
  1434. int jj = ii;
  1435. if( ii >= m_Corners.size() )
  1436. jj = 0;
  1437. // Note: parameter values are always mm or inches
  1438. fprintf( aOutput, "%#f,%#f,",
  1439. m_Corners[jj].x * aIu2GbrMacroUnit, -m_Corners[jj].y * aIu2GbrMacroUnit );
  1440. if( curr_line_count_max >= 0
  1441. && ++curr_line_corner_count >= curr_line_count_max )
  1442. {
  1443. fprintf( aOutput, "\n" );
  1444. curr_line_corner_count = 0;
  1445. }
  1446. }
  1447. // output rotation parameter
  1448. fputs( "$1*%\n", aOutput );
  1449. }
  1450. void APER_MACRO_FREEPOLY_LIST::Format( FILE * aOutput, double aIu2GbrMacroUnit )
  1451. {
  1452. for( int idx = 0; idx < AmCount(); idx++ )
  1453. m_AMList[idx].Format( aOutput, aIu2GbrMacroUnit );
  1454. }
  1455. void APER_MACRO_FREEPOLY_LIST::Append( const std::vector<wxPoint>& aPolygon )
  1456. {
  1457. m_AMList.emplace_back( aPolygon, AmCount() );
  1458. }
  1459. int APER_MACRO_FREEPOLY_LIST::FindAm( const std::vector<wxPoint>& aPolygon ) const
  1460. {
  1461. for( int idx = 0; idx < AmCount(); idx++ )
  1462. {
  1463. if( m_AMList[idx].IsSamePoly( aPolygon ) )
  1464. return idx;
  1465. }
  1466. return -1;
  1467. }