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.

962 lines
30 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
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) 2016 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2016 KiCad Developers, see CHANGELOG.TXT for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. /**
  25. * @file common_plotGERBER_functions.cpp
  26. * @brief Common GERBER plot routines.
  27. */
  28. #include <fctsys.h>
  29. #include <gr_basic.h>
  30. #include <trigo.h>
  31. #include <wxstruct.h>
  32. #include <base_struct.h>
  33. #include <common.h>
  34. #include <plot_common.h>
  35. #include <macros.h>
  36. #include <kicad_string.h>
  37. #include <convert_basic_shapes_to_polygon.h>
  38. #include <build_version.h>
  39. #include <plot_auxiliary_data.h>
  40. GERBER_PLOTTER::GERBER_PLOTTER()
  41. {
  42. workFile = NULL;
  43. finalFile = NULL;
  44. currentAperture = apertures.end();
  45. m_apertureAttribute = 0;
  46. // number of digits after the point (number of digits of the mantissa
  47. // Be carefull: the Gerber coordinates are stored in an integer
  48. // so 6 digits (inches) or 5 digits (mm) is a good value
  49. // To avoid overflow, 7 digits (inches) or 6 digits is a max.
  50. // with lower values than 6 digits (inches) or 5 digits (mm),
  51. // Creating self-intersecting polygons from non-intersecting polygons
  52. // happen easily.
  53. m_gerberUnitInch = false;
  54. m_gerberUnitFmt = 6;
  55. m_useX2Attributes = false;
  56. m_useNetAttributes = true;
  57. }
  58. void GERBER_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil,
  59. double aScale, bool aMirror )
  60. {
  61. wxASSERT( aMirror == false );
  62. m_plotMirror = false;
  63. plotOffset = aOffset;
  64. wxASSERT( aScale == 1 ); // aScale parameter is not used in Gerber
  65. plotScale = 1; // Plot scale is *always* 1.0
  66. m_IUsPerDecimil = aIusPerDecimil;
  67. // gives now a default value to iuPerDeviceUnit (because the units of the caller is now known)
  68. // which could be modified later by calling SetGerberCoordinatesFormat()
  69. iuPerDeviceUnit = pow( 10.0, m_gerberUnitFmt ) / ( m_IUsPerDecimil * 10000.0 );
  70. // We don't handle the filmbox, and it's more useful to keep the
  71. // origin at the origin
  72. paperSize.x = 0;
  73. paperSize.y = 0;
  74. SetDefaultLineWidth( 100 * aIusPerDecimil ); // Arbitrary default
  75. }
  76. void GERBER_PLOTTER::SetGerberCoordinatesFormat( int aResolution, bool aUseInches )
  77. {
  78. m_gerberUnitInch = aUseInches;
  79. m_gerberUnitFmt = aResolution;
  80. iuPerDeviceUnit = pow( 10.0, m_gerberUnitFmt ) / ( m_IUsPerDecimil * 10000.0 );
  81. if( ! m_gerberUnitInch )
  82. iuPerDeviceUnit *= 25.4; // gerber output in mm
  83. }
  84. void GERBER_PLOTTER::emitDcode( const DPOINT& pt, int dcode )
  85. {
  86. fprintf( outputFile, "X%dY%dD%02d*\n",
  87. KiROUND( pt.x ), KiROUND( pt.y ), dcode );
  88. }
  89. void GERBER_PLOTTER::clearNetAttribute()
  90. {
  91. // disable a Gerber net attribute (exists only in X2 with net attributes mode).
  92. if( m_objectAttributesDictionnary.empty() ) // No net attribute or not X2 mode
  93. return;
  94. // Remove all net attributes from object attributes dictionnary
  95. fputs( "%TD*%\n", outputFile );
  96. m_objectAttributesDictionnary.clear();
  97. }
  98. void GERBER_PLOTTER::StartBlock( void* aData )
  99. {
  100. // Currently, it is the same as EndBlock(): clear all aperture net attributes
  101. EndBlock( aData );
  102. }
  103. void GERBER_PLOTTER::EndBlock( void* aData )
  104. {
  105. // Remove all net attributes from object attributes dictionnary
  106. clearNetAttribute();
  107. }
  108. void GERBER_PLOTTER::formatNetAttribute( GBR_NETLIST_METADATA* aData )
  109. {
  110. // print a Gerber net attribute record.
  111. // it is added to the object attributes dictionnary
  112. // On file, only modified or new attributes are printed.
  113. if( aData == NULL || !m_useX2Attributes || !m_useNetAttributes )
  114. return;
  115. bool clearDict;
  116. std::string short_attribute_string;
  117. if( !FormatNetAttribute( short_attribute_string, m_objectAttributesDictionnary,
  118. aData, clearDict ) )
  119. return;
  120. if( clearDict )
  121. clearNetAttribute();
  122. if( !short_attribute_string.empty() )
  123. fputs( short_attribute_string.c_str(), outputFile );
  124. }
  125. bool GERBER_PLOTTER::StartPlot()
  126. {
  127. wxASSERT( outputFile );
  128. finalFile = outputFile; // the actual gerber file will be created later
  129. // Create a temporary filename to store gerber file
  130. // note tmpfile() does not work under Vista and W7 in user mode
  131. m_workFilename = filename + wxT(".tmp");
  132. workFile = wxFopen( m_workFilename, wxT( "wt" ));
  133. outputFile = workFile;
  134. wxASSERT( outputFile );
  135. if( outputFile == NULL )
  136. return false;
  137. for( unsigned ii = 0; ii < m_headerExtraLines.GetCount(); ii++ )
  138. {
  139. if( ! m_headerExtraLines[ii].IsEmpty() )
  140. fprintf( outputFile, "%s\n", TO_UTF8( m_headerExtraLines[ii] ) );
  141. }
  142. // Set coordinate format to 3.6 or 4.5 absolute, leading zero omitted
  143. // the number of digits for the integer part of coordintes is needed
  144. // in gerber format, but is not very important when omitting leading zeros
  145. // It is fixed here to 3 (inch) or 4 (mm), but is not actually used
  146. int leadingDigitCount = m_gerberUnitInch ? 3 : 4;
  147. fprintf( outputFile, "%%FSLAX%d%dY%d%d*%%\n",
  148. leadingDigitCount, m_gerberUnitFmt,
  149. leadingDigitCount, m_gerberUnitFmt );
  150. fprintf( outputFile,
  151. "G04 Gerber Fmt %d.%d, Leading zero omitted, Abs format (unit %s)*\n",
  152. leadingDigitCount, m_gerberUnitFmt,
  153. m_gerberUnitInch ? "inch" : "mm" );
  154. wxString Title = creator + wxT( " " ) + GetBuildVersion();
  155. fprintf( outputFile, "G04 Created by KiCad (%s) date %s*\n",
  156. TO_UTF8( Title ), TO_UTF8( DateAndTime() ) );
  157. /* Mass parameter: unit = INCHES/MM */
  158. if( m_gerberUnitInch )
  159. fputs( "%MOIN*%\n", outputFile );
  160. else
  161. fputs( "%MOMM*%\n", outputFile );
  162. // Be sure the usual dark polarity is selected:
  163. fputs( "%LPD*%\n", outputFile );
  164. // Specify linear interpol (G01):
  165. fputs( "G01*\n", outputFile );
  166. fputs( "G04 APERTURE LIST*\n", outputFile );
  167. /* Select the default aperture */
  168. SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, 0 );
  169. return true;
  170. }
  171. bool GERBER_PLOTTER::EndPlot()
  172. {
  173. char line[1024];
  174. wxString msg;
  175. wxASSERT( outputFile );
  176. /* Outfile is actually a temporary file i.e. workFile */
  177. fputs( "M02*\n", outputFile );
  178. fflush( outputFile );
  179. fclose( workFile );
  180. workFile = wxFopen( m_workFilename, wxT( "rt" ));
  181. wxASSERT( workFile );
  182. outputFile = finalFile;
  183. // Placement of apertures in RS274X
  184. while( fgets( line, 1024, workFile ) )
  185. {
  186. fputs( line, outputFile );
  187. if( strcmp( strtok( line, "\n\r" ), "G04 APERTURE LIST*" ) == 0 )
  188. {
  189. writeApertureList();
  190. fputs( "G04 APERTURE END LIST*\n", outputFile );
  191. }
  192. }
  193. fclose( workFile );
  194. fclose( finalFile );
  195. ::wxRemoveFile( m_workFilename );
  196. outputFile = 0;
  197. return true;
  198. }
  199. void GERBER_PLOTTER::SetDefaultLineWidth( int width )
  200. {
  201. defaultPenWidth = width;
  202. currentAperture = apertures.end();
  203. }
  204. void GERBER_PLOTTER::SetCurrentLineWidth( int width, void* aData )
  205. {
  206. if( width == DO_NOT_SET_LINE_WIDTH )
  207. return;
  208. int pen_width;
  209. if( width > 0 )
  210. pen_width = width;
  211. else
  212. pen_width = defaultPenWidth;
  213. GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
  214. int aperture_attribute = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
  215. selectAperture( wxSize( pen_width, pen_width ), APERTURE::Plotting, aperture_attribute );
  216. currentPenWidth = pen_width;
  217. }
  218. std::vector<APERTURE>::iterator GERBER_PLOTTER::getAperture( const wxSize& aSize,
  219. APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
  220. {
  221. int last_D_code = 9;
  222. // Search an existing aperture
  223. std::vector<APERTURE>::iterator tool = apertures.begin();
  224. while( tool != apertures.end() )
  225. {
  226. last_D_code = tool->m_DCode;
  227. if( (tool->m_Type == aType) && (tool->m_Size == aSize) && (tool->m_ApertureAttribute == aApertureAttribute) )
  228. return tool;
  229. ++tool;
  230. }
  231. // Allocate a new aperture
  232. APERTURE new_tool;
  233. new_tool.m_Size = aSize;
  234. new_tool.m_Type = aType;
  235. new_tool.m_DCode = last_D_code + 1;
  236. new_tool.m_ApertureAttribute = aApertureAttribute;
  237. apertures.push_back( new_tool );
  238. return apertures.end() - 1;
  239. }
  240. void GERBER_PLOTTER::selectAperture( const wxSize& aSize,
  241. APERTURE::APERTURE_TYPE aType,
  242. int aApertureAttribute )
  243. {
  244. bool change = ( currentAperture == apertures.end() ) ||
  245. ( currentAperture->m_Type != aType ) ||
  246. ( currentAperture->m_Size != aSize );
  247. if( !m_useX2Attributes || !m_useNetAttributes )
  248. aApertureAttribute = 0;
  249. else
  250. change = change || ( currentAperture->m_ApertureAttribute != aApertureAttribute );
  251. if( change )
  252. {
  253. // Pick an existing aperture or create a new one
  254. currentAperture = getAperture( aSize, aType, aApertureAttribute );
  255. fprintf( outputFile, "D%d*\n", currentAperture->m_DCode );
  256. }
  257. }
  258. void GERBER_PLOTTER::writeApertureList()
  259. {
  260. wxASSERT( outputFile );
  261. char cbuf[1024];
  262. // Init
  263. for( std::vector<APERTURE>::iterator tool = apertures.begin();
  264. tool != apertures.end(); ++tool )
  265. {
  266. // apertude sizes are in inch or mm, regardless the
  267. // coordinates format
  268. double fscale = 0.0001 * plotScale / m_IUsPerDecimil; // inches
  269. if(! m_gerberUnitInch )
  270. fscale *= 25.4; // size in mm
  271. int attribute = tool->m_ApertureAttribute;
  272. if( attribute != m_apertureAttribute )
  273. fputs( GBR_APERTURE_METADATA::FormatAttribute(
  274. (GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB) attribute ).c_str(), outputFile );
  275. char* text = cbuf + sprintf( cbuf, "%%ADD%d", tool->m_DCode );
  276. /* Please note: the Gerber specs for mass parameters say that
  277. exponential syntax is *not* allowed and the decimal point should
  278. also be always inserted. So the %g format is ruled out, but %f is fine
  279. (the # modifier forces the decimal point). Sadly the %f formatter
  280. can't remove trailing zeros but thats not a problem, since nothing
  281. forbid it (the file is only slightly longer) */
  282. switch( tool->m_Type )
  283. {
  284. case APERTURE::Circle:
  285. sprintf( text, "C,%#f*%%\n", tool->m_Size.x * fscale );
  286. break;
  287. case APERTURE::Rect:
  288. sprintf( text, "R,%#fX%#f*%%\n",
  289. tool->m_Size.x * fscale,
  290. tool->m_Size.y * fscale );
  291. break;
  292. case APERTURE::Plotting:
  293. sprintf( text, "C,%#f*%%\n", tool->m_Size.x * fscale );
  294. break;
  295. case APERTURE::Oval:
  296. sprintf( text, "O,%#fX%#f*%%\n",
  297. tool->m_Size.x * fscale,
  298. tool->m_Size.y * fscale );
  299. break;
  300. }
  301. fputs( cbuf, outputFile );
  302. m_apertureAttribute = attribute;
  303. // Currently reset the aperture attribute. Perhaps a better optimization
  304. // is to store the last attribute
  305. if( attribute )
  306. {
  307. fputs( "%TD*%\n", outputFile );
  308. m_apertureAttribute = 0;
  309. }
  310. }
  311. }
  312. void GERBER_PLOTTER::PenTo( const wxPoint& aPos, char plume )
  313. {
  314. wxASSERT( outputFile );
  315. DPOINT pos_dev = userToDeviceCoordinates( aPos );
  316. switch( plume )
  317. {
  318. case 'Z':
  319. break;
  320. case 'U':
  321. emitDcode( pos_dev, 2 );
  322. break;
  323. case 'D':
  324. emitDcode( pos_dev, 1 );
  325. }
  326. penState = plume;
  327. }
  328. void GERBER_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width )
  329. {
  330. std::vector< wxPoint > cornerList;
  331. // Build corners list
  332. cornerList.push_back( p1 );
  333. wxPoint corner(p1.x, p2.y);
  334. cornerList.push_back( corner );
  335. cornerList.push_back( p2 );
  336. corner.x = p2.x;
  337. corner.y = p1.y;
  338. cornerList.push_back( corner );
  339. cornerList.push_back( p1 );
  340. PlotPoly( cornerList, fill, width );
  341. }
  342. void GERBER_PLOTTER::Circle( const wxPoint& aCenter, int aDiameter, FILL_T aFill, int aWidth )
  343. {
  344. Arc( aCenter, 0, 3600, aDiameter / 2, aFill, aWidth );
  345. }
  346. void GERBER_PLOTTER::Arc( const wxPoint& aCenter, double aStAngle, double aEndAngle,
  347. int aRadius, FILL_T aFill, int aWidth )
  348. {
  349. SetCurrentLineWidth( aWidth );
  350. wxPoint start, end;
  351. start.x = aCenter.x + KiROUND( cosdecideg( aRadius, aStAngle ) );
  352. start.y = aCenter.y - KiROUND( sindecideg( aRadius, aStAngle ) );
  353. MoveTo( start );
  354. end.x = aCenter.x + KiROUND( cosdecideg( aRadius, aEndAngle ) );
  355. end.y = aCenter.y - KiROUND( sindecideg( aRadius, aEndAngle ) );
  356. DPOINT devEnd = userToDeviceCoordinates( end );
  357. DPOINT devCenter = userToDeviceCoordinates( aCenter ) - userToDeviceCoordinates( start );
  358. fprintf( outputFile, "G75*\n" ); // Multiquadrant mode
  359. if( aStAngle < aEndAngle )
  360. fprintf( outputFile, "G03" );
  361. else
  362. fprintf( outputFile, "G02" );
  363. fprintf( outputFile, "X%dY%dI%dJ%dD01*\n",
  364. KiROUND( devEnd.x ), KiROUND( devEnd.y ),
  365. KiROUND( devCenter.x ), KiROUND( devCenter.y ) );
  366. fprintf( outputFile, "G01*\n" ); // Back to linear interp.
  367. }
  368. void GERBER_PLOTTER:: PlotPoly( const std::vector< wxPoint >& aCornerList,
  369. FILL_T aFill, int aWidth, void * aData )
  370. {
  371. if( aCornerList.size() <= 1 )
  372. return;
  373. // Gerber format does not know filled polygons with thick outline
  374. // Therefore, to plot a filled polygon with outline having a thickness,
  375. // one should plot outline as thick segments
  376. GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
  377. SetCurrentLineWidth( aWidth, gbr_metadata );
  378. if( gbr_metadata )
  379. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  380. if( aFill )
  381. {
  382. fputs( "G36*\n", outputFile );
  383. MoveTo( aCornerList[0] );
  384. for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
  385. LineTo( aCornerList[ii] );
  386. FinishTo( aCornerList[0] );
  387. fputs( "G37*\n", outputFile );
  388. }
  389. if( aWidth > 0 )
  390. {
  391. MoveTo( aCornerList[0] );
  392. for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
  393. LineTo( aCornerList[ii] );
  394. // Ensure the thick outline is closed for filled polygons
  395. // (if not filled, could be only a polyline)
  396. if( aFill && ( aCornerList[aCornerList.size()-1] != aCornerList[0] ) )
  397. LineTo( aCornerList[0] );
  398. PenFinish();
  399. }
  400. }
  401. void GERBER_PLOTTER::ThickSegment( const wxPoint& start, const wxPoint& end, int width,
  402. EDA_DRAW_MODE_T tracemode, void* aData )
  403. {
  404. if( tracemode == FILLED )
  405. {
  406. GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
  407. SetCurrentLineWidth( width, gbr_metadata );
  408. if( gbr_metadata )
  409. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  410. MoveTo( start );
  411. FinishTo( end );
  412. }
  413. else
  414. {
  415. SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH );
  416. segmentAsOval( start, end, width, tracemode );
  417. }
  418. }
  419. void GERBER_PLOTTER::ThickArc( const wxPoint& centre, double StAngle, double EndAngle,
  420. int radius, int width, EDA_DRAW_MODE_T tracemode, void* aData )
  421. {
  422. GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
  423. SetCurrentLineWidth( width, gbr_metadata );
  424. if( gbr_metadata )
  425. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  426. if( tracemode == FILLED )
  427. Arc( centre, StAngle, EndAngle, radius, NO_FILL, DO_NOT_SET_LINE_WIDTH );
  428. else
  429. {
  430. SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH );
  431. Arc( centre, StAngle, EndAngle,
  432. radius - ( width - currentPenWidth ) / 2,
  433. NO_FILL, DO_NOT_SET_LINE_WIDTH );
  434. Arc( centre, StAngle, EndAngle,
  435. radius + ( width - currentPenWidth ) / 2, NO_FILL,
  436. DO_NOT_SET_LINE_WIDTH );
  437. }
  438. }
  439. void GERBER_PLOTTER::ThickRect( const wxPoint& p1, const wxPoint& p2, int width,
  440. EDA_DRAW_MODE_T tracemode, void* aData )
  441. {
  442. GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
  443. SetCurrentLineWidth( width, gbr_metadata );
  444. if( gbr_metadata )
  445. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  446. if( tracemode == FILLED )
  447. Rect( p1, p2, NO_FILL, DO_NOT_SET_LINE_WIDTH );
  448. else
  449. {
  450. SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH );
  451. wxPoint offsetp1( p1.x - (width - currentPenWidth) / 2,
  452. p1.y - (width - currentPenWidth) / 2 );
  453. wxPoint offsetp2( p2.x + (width - currentPenWidth) / 2,
  454. p2.y + (width - currentPenWidth) / 2 );
  455. Rect( offsetp1, offsetp2, NO_FILL, -1 );
  456. offsetp1.x += (width - currentPenWidth);
  457. offsetp1.y += (width - currentPenWidth);
  458. offsetp2.x -= (width - currentPenWidth);
  459. offsetp2.y -= (width - currentPenWidth);
  460. Rect( offsetp1, offsetp2, NO_FILL, DO_NOT_SET_LINE_WIDTH );
  461. }
  462. }
  463. void GERBER_PLOTTER::ThickCircle( const wxPoint& pos, int diametre, int width,
  464. EDA_DRAW_MODE_T tracemode, void* aData )
  465. {
  466. GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData );
  467. SetCurrentLineWidth( width, gbr_metadata );
  468. if( gbr_metadata )
  469. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  470. if( tracemode == FILLED )
  471. Circle( pos, diametre, NO_FILL, DO_NOT_SET_LINE_WIDTH );
  472. else
  473. {
  474. SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, gbr_metadata );
  475. Circle( pos, diametre - (width - currentPenWidth),
  476. NO_FILL, DO_NOT_SET_LINE_WIDTH );
  477. Circle( pos, diametre + (width - currentPenWidth),
  478. NO_FILL, DO_NOT_SET_LINE_WIDTH );
  479. }
  480. }
  481. void GERBER_PLOTTER::FlashPadCircle( const wxPoint& pos, int diametre, EDA_DRAW_MODE_T trace_mode, void* aData )
  482. {
  483. wxSize size( diametre, diametre );
  484. GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
  485. if( trace_mode == SKETCH )
  486. {
  487. SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, gbr_metadata );
  488. if( gbr_metadata )
  489. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  490. Circle( pos, diametre - currentPenWidth, NO_FILL, DO_NOT_SET_LINE_WIDTH );
  491. }
  492. else
  493. {
  494. DPOINT pos_dev = userToDeviceCoordinates( pos );
  495. int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
  496. selectAperture( size, APERTURE::Circle, aperture_attrib );
  497. if( gbr_metadata )
  498. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  499. emitDcode( pos_dev, 3 );
  500. }
  501. }
  502. void GERBER_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double orient,
  503. EDA_DRAW_MODE_T trace_mode, void* aData )
  504. {
  505. wxASSERT( outputFile );
  506. int x0, y0, x1, y1, delta;
  507. wxSize size( aSize );
  508. GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
  509. /* Plot a flashed shape. */
  510. if( ( orient == 0 || orient == 900 || orient == 1800 || orient == 2700 )
  511. && trace_mode == FILLED )
  512. {
  513. if( orient == 900 || orient == 2700 ) /* orientation turned 90 deg. */
  514. std::swap( size.x, size.y );
  515. DPOINT pos_dev = userToDeviceCoordinates( pos );
  516. int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
  517. selectAperture( size, APERTURE::Oval, aperture_attrib );
  518. if( gbr_metadata )
  519. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  520. emitDcode( pos_dev, 3 );
  521. }
  522. else /* Plot pad as a segment. */
  523. {
  524. if( size.x > size.y )
  525. {
  526. std::swap( size.x, size.y );
  527. if( orient < 2700 )
  528. orient += 900;
  529. else
  530. orient -= 2700;
  531. }
  532. if( trace_mode == FILLED )
  533. {
  534. // TODO: use an aperture macro to declare the rotated pad
  535. //
  536. // Flash a pad anchor, if a netlist attribute is set
  537. if( aData )
  538. FlashPadCircle( pos, size.x, trace_mode, aData );
  539. // The pad is reduced to an segment with dy > dx
  540. delta = size.y - size.x;
  541. x0 = 0;
  542. y0 = -delta / 2;
  543. x1 = 0;
  544. y1 = delta / 2;
  545. RotatePoint( &x0, &y0, orient );
  546. RotatePoint( &x1, &y1, orient );
  547. GBR_METADATA metadata;
  548. if( gbr_metadata )
  549. {
  550. metadata = *gbr_metadata;
  551. metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR );
  552. // Cleat .P attribute, only allowed for flashed items
  553. wxString attrname( ".P" );
  554. metadata.m_NetlistMetadata.ClearAttribute( &attrname );
  555. }
  556. ThickSegment( wxPoint( pos.x + x0, pos.y + y0 ),
  557. wxPoint( pos.x + x1, pos.y + y1 ),
  558. size.x, trace_mode, &metadata );
  559. }
  560. else
  561. {
  562. sketchOval( pos, size, orient, -1 );
  563. }
  564. }
  565. }
  566. void GERBER_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& aSize,
  567. double orient, EDA_DRAW_MODE_T trace_mode, void* aData )
  568. {
  569. wxASSERT( outputFile );
  570. wxSize size( aSize );
  571. GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
  572. // Plot as an aperture flash
  573. switch( int( orient ) )
  574. {
  575. case 900:
  576. case 2700: // rotation of 90 degrees or 270 swaps sizes
  577. std::swap( size.x, size.y );
  578. // Pass through
  579. case 0:
  580. case 1800:
  581. if( trace_mode == SKETCH )
  582. {
  583. SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, gbr_metadata );
  584. if( gbr_metadata )
  585. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  586. Rect( wxPoint( pos.x - (size.x - currentPenWidth) / 2,
  587. pos.y - (size.y - currentPenWidth) / 2 ),
  588. wxPoint( pos.x + (size.x - currentPenWidth) / 2,
  589. pos.y + (size.y - currentPenWidth) / 2 ),
  590. NO_FILL );
  591. }
  592. else
  593. {
  594. DPOINT pos_dev = userToDeviceCoordinates( pos );
  595. int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
  596. selectAperture( size, APERTURE::Rect, aperture_attrib );
  597. if( gbr_metadata )
  598. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  599. emitDcode( pos_dev, 3 );
  600. }
  601. break;
  602. default: // plot pad shape as polygon
  603. {
  604. // XXX to do: use an aperture macro to declare the rotated pad
  605. wxPoint coord[4];
  606. // coord[0] is assumed the lower left
  607. // coord[1] is assumed the upper left
  608. // coord[2] is assumed the upper right
  609. // coord[3] is assumed the lower right
  610. /* Trace the outline. */
  611. coord[0].x = -size.x/2; // lower left
  612. coord[0].y = size.y/2;
  613. coord[1].x = -size.x/2; // upper left
  614. coord[1].y = -size.y/2;
  615. coord[2].x = size.x/2; // upper right
  616. coord[2].y = -size.y/2;
  617. coord[3].x = size.x/2; // lower right
  618. coord[3].y = size.y/2;
  619. FlashPadTrapez( pos, coord, orient, trace_mode, aData );
  620. }
  621. break;
  622. }
  623. }
  624. void GERBER_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSize,
  625. int aCornerRadius, double aOrient,
  626. EDA_DRAW_MODE_T aTraceMode, void* aData )
  627. {
  628. // Currently, a Pad RoundRect is plotted as polygon.
  629. // TODO: use Aperture macro and flash it
  630. SHAPE_POLY_SET outline;
  631. const int segmentToCircleCount = 64;
  632. TransformRoundRectToPolygon( outline, aPadPos, aSize, aOrient,
  633. aCornerRadius, segmentToCircleCount );
  634. std::vector< wxPoint > cornerList;
  635. cornerList.reserve( segmentToCircleCount + 5 );
  636. // TransformRoundRectToPolygon creates only one convex polygon
  637. SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
  638. for( int ii = 0; ii < poly.PointCount(); ++ii )
  639. cornerList.push_back( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) );
  640. // Close polygon
  641. cornerList.push_back( cornerList[0] );
  642. GBR_METADATA gbr_metadata;
  643. if( aData )
  644. {
  645. gbr_metadata = *static_cast<GBR_METADATA*>( aData );
  646. gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR );
  647. wxString attrname( ".P" );
  648. gbr_metadata.m_NetlistMetadata.ClearAttribute( &attrname ); // not allowed on inner layers
  649. }
  650. PlotPoly( cornerList, ( aTraceMode == FILLED ) ? FILLED_SHAPE : NO_FILL, USE_DEFAULT_LINE_WIDTH, &gbr_metadata );
  651. // Now, flash a pad anchor, if a netlist attribute is set
  652. // (remove me when a Aperture macro will be used)
  653. if( aData && aTraceMode == FILLED )
  654. {
  655. int diameter = std::min( aSize.x, aSize.y );
  656. FlashPadCircle( aPadPos, diameter, aTraceMode , aData );
  657. }
  658. }
  659. void GERBER_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize,
  660. SHAPE_POLY_SET* aPolygons,
  661. EDA_DRAW_MODE_T aTraceMode, void* aData )
  662. {
  663. // A Pad custom is plotted as polygon.
  664. // A flashed circle @aPadPos is added (anchor pad)
  665. // However, because the anchor pad can be circle or rect, we use only
  666. // a circle not bigger than the rect.
  667. // the main purpose is to print a flashed DCode as pad anchor
  668. FlashPadCircle( aPadPos, std::min( aSize.x, aSize.x ), aTraceMode, aData );
  669. GBR_METADATA gbr_metadata;
  670. if( aData )
  671. {
  672. gbr_metadata = *static_cast<GBR_METADATA*>( aData );
  673. gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR );
  674. wxString attrname( ".P" );
  675. gbr_metadata.m_NetlistMetadata.ClearAttribute( &attrname ); // not allowed on inner layers
  676. }
  677. std::vector< wxPoint > cornerList;
  678. for( int cnt = 0; cnt < aPolygons->OutlineCount(); ++cnt )
  679. {
  680. SHAPE_LINE_CHAIN& poly = aPolygons->Outline( cnt );
  681. cornerList.clear();
  682. for( int ii = 0; ii < poly.PointCount(); ++ii )
  683. cornerList.push_back( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) );
  684. // Close polygon
  685. cornerList.push_back( cornerList[0] );
  686. PlotPoly( cornerList, ( aTraceMode == FILLED ) ? FILLED_SHAPE : NO_FILL, USE_DEFAULT_LINE_WIDTH, &gbr_metadata );
  687. }
  688. }
  689. void GERBER_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCorners,
  690. double aPadOrient, EDA_DRAW_MODE_T aTrace_Mode, void* aData )
  691. {
  692. // Currently, a Pad Trapezoid is plotted as polygon.
  693. // TODO: use Aperture macro and flash it
  694. // polygon corners list
  695. std::vector< wxPoint > cornerList;
  696. for( int ii = 0; ii < 4; ii++ )
  697. cornerList.push_back( aCorners[ii] );
  698. // Now, flash a pad anchor, if a netlist attribute is set
  699. // (remove me when a Aperture macro will be used)
  700. if( aData && (aTrace_Mode==FILLED) )
  701. {
  702. // Calculate the radius of the circle inside the shape
  703. // It is the smaller dist from shape pos to edges
  704. int radius = INT_MAX;
  705. for( unsigned ii = 0, jj = cornerList.size()-1; ii < cornerList.size();
  706. jj = ii, ii++ )
  707. {
  708. SEG segment( aCorners[ii], aCorners[jj] );
  709. int dist = segment.LineDistance( VECTOR2I( 0, 0) );
  710. radius = std::min( radius, dist );
  711. }
  712. FlashPadCircle( aPadPos, radius*2, aTrace_Mode, aData );
  713. }
  714. // Draw the polygon and fill the interior as required
  715. for( unsigned ii = 0; ii < 4; ii++ )
  716. {
  717. RotatePoint( &cornerList[ii], aPadOrient );
  718. cornerList[ii] += aPadPos;
  719. }
  720. // Close the polygon
  721. cornerList.push_back( cornerList[0] );
  722. GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
  723. GBR_METADATA metadata;
  724. if( gbr_metadata )
  725. {
  726. metadata = *gbr_metadata;
  727. metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR );
  728. wxString attrname( ".P" );
  729. metadata.m_NetlistMetadata.ClearAttribute( &attrname ); // not allowed on inner layers
  730. }
  731. SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &metadata );
  732. PlotPoly( cornerList, aTrace_Mode==FILLED ? FILLED_SHAPE : NO_FILL, USE_DEFAULT_LINE_WIDTH, &metadata );
  733. }
  734. void GERBER_PLOTTER::Text( const wxPoint& aPos, const COLOR4D aColor,
  735. const wxString& aText, double aOrient, const wxSize& aSize,
  736. enum EDA_TEXT_HJUSTIFY_T aH_justify, enum EDA_TEXT_VJUSTIFY_T aV_justify,
  737. int aWidth, bool aItalic, bool aBold, bool aMultilineAllowed,
  738. void* aData )
  739. {
  740. GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
  741. if( gbr_metadata )
  742. formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
  743. PLOTTER::Text( aPos, aColor, aText, aOrient, aSize,
  744. aH_justify, aV_justify, aWidth, aItalic, aBold, aMultilineAllowed, aData );
  745. }
  746. void GERBER_PLOTTER::SetLayerPolarity( bool aPositive )
  747. {
  748. if( aPositive )
  749. fprintf( outputFile, "%%LPD*%%\n" );
  750. else
  751. fprintf( outputFile, "%%LPC*%%\n" );
  752. }