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.

900 lines
32 KiB

// Dick Hollenbeck's KiROUND R&D // This provides better project control over rounding to int from double // than wxRound() did. This scheme provides better logging in Debug builds // and it provides for compile time calculation of constants. #include <stdio.h> #include <assert.h> #include <limits.h> //-----<KiROUND KIT>------------------------------------------------------------ /** * KiROUND * rounds a floating point number to an int using * "round halfway cases away from zero". * In Debug build an assert fires if will not fit into an int. */ #if defined( DEBUG ) // DEBUG: a macro to capture line and file, then calls this inline static inline int KiRound( double v, int line, const char* filename ) { v = v < 0 ? v - 0.5 : v + 0.5; if( v > INT_MAX + 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v ); } else if( v < INT_MIN - 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v ); } return int( v ); } #define KiROUND( v ) KiRound( v, __LINE__, __FILE__ ) #else // RELEASE: a macro so compile can pre-compute constants. #define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 ) #endif //-----</KiROUND KIT>----------------------------------------------------------- // Only a macro is compile time calculated, an inline function causes a static constructor // in a situation like this. // Therefore the Release build is best done with a MACRO not an inline function. int Computed = KiROUND( 14.3 * 8 ); int main( int argc, char** argv ) { for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 ) { int i = KiROUND( d ); printf( "t: %d %.16g\n", i, d ); } return 0; }
14 years ago
// Dick Hollenbeck's KiROUND R&D // This provides better project control over rounding to int from double // than wxRound() did. This scheme provides better logging in Debug builds // and it provides for compile time calculation of constants. #include <stdio.h> #include <assert.h> #include <limits.h> //-----<KiROUND KIT>------------------------------------------------------------ /** * KiROUND * rounds a floating point number to an int using * "round halfway cases away from zero". * In Debug build an assert fires if will not fit into an int. */ #if defined( DEBUG ) // DEBUG: a macro to capture line and file, then calls this inline static inline int KiRound( double v, int line, const char* filename ) { v = v < 0 ? v - 0.5 : v + 0.5; if( v > INT_MAX + 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v ); } else if( v < INT_MIN - 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v ); } return int( v ); } #define KiROUND( v ) KiRound( v, __LINE__, __FILE__ ) #else // RELEASE: a macro so compile can pre-compute constants. #define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 ) #endif //-----</KiROUND KIT>----------------------------------------------------------- // Only a macro is compile time calculated, an inline function causes a static constructor // in a situation like this. // Therefore the Release build is best done with a MACRO not an inline function. int Computed = KiROUND( 14.3 * 8 ); int main( int argc, char** argv ) { for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 ) { int i = KiROUND( d ); printf( "t: %d %.16g\n", i, d ); } return 0; }
14 years ago
  1. /**
  2. * @file aperture_macro.cpp
  3. */
  4. /*
  5. * This program source code file is part of KiCad, a free EDA CAD application.
  6. *
  7. * Copyright (C) 1992-2017 Jean-Pierre Charras <jp.charras at wanadoo.fr>
  8. * Copyright (C) 2010 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  9. * Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors.
  10. *
  11. * This program is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU General Public License
  13. * as published by the Free Software Foundation; either version 2
  14. * of the License, or (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program; if not, you may find one here:
  23. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  24. * or you may search the http://www.gnu.org website for the version 2 license,
  25. * or you may write to the Free Software Foundation, Inc.,
  26. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  27. */
  28. #include <fctsys.h>
  29. #include <common.h>
  30. #include <macros.h>
  31. #include <trigo.h>
  32. #include <convert_basic_shapes_to_polygon.h>
  33. #include <gr_basic.h>
  34. #include <gerbview.h>
  35. #include <gerber_file_image.h>
  36. /**
  37. * Function scaletoIU
  38. * converts a distance given in floating point to our internal units
  39. */
  40. extern int scaletoIU( double aCoord, bool isMetric ); // defined it rs274d_read_XY_and_IJ_coordiantes.cpp
  41. /**
  42. * Function mapPt
  43. * translates a point from the aperture macro coordinate system to our
  44. * deci-mils coordinate system.
  45. * @return wxPoint - The GerbView coordinate system vector.
  46. */
  47. static wxPoint mapPt( double x, double y, bool isMetric )
  48. {
  49. wxPoint ret( scaletoIU( x, isMetric ), scaletoIU( y, isMetric ) );
  50. return ret;
  51. }
  52. bool AM_PRIMITIVE::IsAMPrimitiveExposureOn( const GERBER_DRAW_ITEM* aParent ) const
  53. {
  54. /*
  55. * Some but not all primitives use the first parameter as an exposure control.
  56. * Others are always ON.
  57. * In a aperture macro shape, a basic primitive with exposure off is a hole in the shape
  58. * it is NOT a negative shape
  59. */
  60. wxASSERT( params.size() );
  61. switch( primitive_id )
  62. {
  63. case AMP_CIRCLE:
  64. case AMP_LINE2:
  65. case AMP_LINE20:
  66. case AMP_LINE_CENTER:
  67. case AMP_LINE_LOWER_LEFT:
  68. case AMP_OUTLINE:
  69. case AMP_POLYGON:
  70. // All have an exposure parameter and can return a value (0 or 1)
  71. return params[0].GetValue( aParent->GetDcodeDescr() ) != 0;
  72. break;
  73. case AMP_THERMAL: // Exposure is always on
  74. case AMP_MOIRE: // Exposure is always on
  75. case AMP_EOF:
  76. case AMP_UNKNOWN:
  77. default:
  78. return 1; // All have no exposure parameter and are always 0N return true
  79. break;
  80. }
  81. }
  82. const int seg_per_circle = 64; // Number of segments to approximate a circle
  83. void AM_PRIMITIVE::DrawBasicShape( const GERBER_DRAW_ITEM* aParent,
  84. SHAPE_POLY_SET& aShapeBuffer,
  85. wxPoint aShapePos )
  86. {
  87. #define TO_POLY_SHAPE { aShapeBuffer.NewOutline(); \
  88. for( unsigned jj = 0; jj < polybuffer.size(); jj++ )\
  89. aShapeBuffer.Append( polybuffer[jj].x, polybuffer[jj].y );\
  90. aShapeBuffer.Append( polybuffer[0].x, polybuffer[0].y );}
  91. // Draw the primitive shape for flashed items.
  92. static std::vector<wxPoint> polybuffer; // create a static buffer to avoid a lot of memory reallocation
  93. polybuffer.clear();
  94. wxPoint curPos = aShapePos;
  95. D_CODE* tool = aParent->GetDcodeDescr();
  96. double rotation;
  97. switch( primitive_id )
  98. {
  99. case AMP_CIRCLE: // Circle, given diameter and position
  100. {
  101. /* Generated by an aperture macro declaration like:
  102. * "1,1,0.3,0.5, 1.0*"
  103. * type (1), exposure, diameter, pos.x, pos.y, <rotation>
  104. * <rotation> is a optional parameter: rotation from origin.
  105. * type is not stored in parameters list, so the first parameter is exposure
  106. */
  107. ConvertShapeToPolygon( aParent, polybuffer );
  108. // shape rotation (if any):
  109. if( params.size() >= 5 )
  110. {
  111. rotation = params[4].GetValue( tool ) * 10.0;
  112. if( rotation != 0)
  113. {
  114. for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
  115. RotatePoint( &polybuffer[ii], -rotation );
  116. }
  117. }
  118. // Move to current position:
  119. for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
  120. {
  121. polybuffer[ii] += curPos;
  122. polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
  123. }
  124. TO_POLY_SHAPE;
  125. }
  126. break;
  127. case AMP_LINE2:
  128. case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation)
  129. {
  130. /* Vector Line, Primitive Code 20.
  131. * A vector line is a rectangle defined by its line width, start and end points.
  132. * The line ends are rectangular.
  133. */
  134. /* Generated by an aperture macro declaration like:
  135. * "2,1,0.3,0,0, 0.5, 1.0,-135*"
  136. * type (2), exposure, width, start.x, start.y, end.x, end.y, rotation
  137. * type is not stored in parameters list, so the first parameter is exposure
  138. */
  139. ConvertShapeToPolygon( aParent, polybuffer );
  140. // shape rotation:
  141. rotation = params[6].GetValue( tool ) * 10.0;
  142. if( rotation != 0)
  143. {
  144. for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
  145. RotatePoint( &polybuffer[ii], -rotation );
  146. }
  147. // Move to current position:
  148. for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
  149. {
  150. polybuffer[ii] += curPos;
  151. polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
  152. }
  153. TO_POLY_SHAPE;
  154. }
  155. break;
  156. case AMP_LINE_CENTER:
  157. {
  158. /* Center Line, Primitive Code 21
  159. * A center line primitive is a rectangle defined by its width, height, and center point
  160. */
  161. /* Generated by an aperture macro declaration like:
  162. * "21,1,0.3,0.03,0,0,-135*"
  163. * type (21), exposure, ,width, height, center pos.x, center pos.y, rotation
  164. * type is not stored in parameters list, so the first parameter is exposure
  165. */
  166. ConvertShapeToPolygon( aParent, polybuffer );
  167. // shape rotation:
  168. rotation = params[5].GetValue( tool ) * 10.0;
  169. if( rotation != 0 )
  170. {
  171. for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
  172. RotatePoint( &polybuffer[ii], -rotation );
  173. }
  174. // Move to current position:
  175. for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
  176. {
  177. polybuffer[ii] += curPos;
  178. polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
  179. }
  180. TO_POLY_SHAPE;
  181. }
  182. break;
  183. case AMP_LINE_LOWER_LEFT:
  184. {
  185. /* Generated by an aperture macro declaration like:
  186. * "22,1,0.3,0.03,0,0,-135*"
  187. * type (22), exposure, ,width, height, corner pos.x, corner pos.y, rotation
  188. * type is not stored in parameters list, so the first parameter is exposure
  189. */
  190. ConvertShapeToPolygon( aParent, polybuffer );
  191. // shape rotation:
  192. rotation = params[5].GetValue( tool ) * 10.0;
  193. if( rotation != 0)
  194. {
  195. for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
  196. RotatePoint( &polybuffer[ii], -rotation );
  197. }
  198. // Move to current position:
  199. for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
  200. {
  201. polybuffer[ii] += curPos;
  202. polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
  203. }
  204. TO_POLY_SHAPE;
  205. }
  206. break;
  207. case AMP_THERMAL:
  208. {
  209. /* Generated by an aperture macro declaration like:
  210. * "7, 0,0,1.0,0.3,0.01,-13*"
  211. * type (7), center.x , center.y, outside diam, inside diam, crosshair thickness, rotation
  212. * type is not stored in parameters list, so the first parameter is center.x
  213. *
  214. * The thermal primitive is a ring (annulus) interrupted by four gaps. Exposure is always on.
  215. */
  216. std::vector<wxPoint> subshape_poly;
  217. curPos += mapPt( params[0].GetValue( tool ), params[1].GetValue( tool ), m_GerbMetric );
  218. ConvertShapeToPolygon( aParent, subshape_poly );
  219. // shape rotation:
  220. rotation = params[5].GetValue( tool ) * 10.0;
  221. // Because a thermal shape has 4 identical sub-shapes, only one is created in subshape_poly.
  222. // We must draw 4 sub-shapes rotated by 90 deg
  223. for( int ii = 0; ii < 4; ii++ )
  224. {
  225. polybuffer = subshape_poly;
  226. double sub_rotation = rotation + 900 * ii;
  227. for( unsigned jj = 0; jj < polybuffer.size(); jj++ )
  228. RotatePoint( &polybuffer[jj], -sub_rotation );
  229. // Move to current position:
  230. for( unsigned jj = 0; jj < polybuffer.size(); jj++ )
  231. {
  232. polybuffer[jj] += curPos;
  233. polybuffer[jj] = aParent->GetABPosition( polybuffer[jj] );
  234. }
  235. TO_POLY_SHAPE;
  236. }
  237. }
  238. break;
  239. case AMP_MOIRE:
  240. {
  241. /* Moir�, Primitive Code 6
  242. * The moir primitive is a cross hair centered on concentric rings (annuli).
  243. * Exposure is always on.
  244. */
  245. curPos += mapPt( params[0].GetValue( tool ), params[1].GetValue( tool ),
  246. m_GerbMetric );
  247. /* Generated by an aperture macro declaration like:
  248. * "6,0,0,0.125,.01,0.01,3,0.003,0.150,0"
  249. * type(6), pos.x, pos.y, diam, penwidth, gap, circlecount, crosshair thickness, crosshaire len, rotation
  250. * type is not stored in parameters list, so the first parameter is pos.x
  251. */
  252. int outerDiam = scaletoIU( params[2].GetValue( tool ), m_GerbMetric );
  253. int penThickness = scaletoIU( params[3].GetValue( tool ), m_GerbMetric );
  254. int gap = scaletoIU( params[4].GetValue( tool ), m_GerbMetric );
  255. int numCircles = KiROUND( params[5].GetValue( tool ) );
  256. // Draw circles:
  257. wxPoint center = aParent->GetABPosition( curPos );
  258. // adjust outerDiam by this on each nested circle
  259. int diamAdjust = (gap + penThickness) * 2;
  260. for( int i = 0; i < numCircles; ++i, outerDiam -= diamAdjust )
  261. {
  262. if( outerDiam <= 0 )
  263. break;
  264. // Note: outerDiam is the outer diameter of the ring.
  265. // the ring graphic diameter is (outerDiam - penThickness)
  266. if( outerDiam <= penThickness )
  267. { // No room to draw a ring (no room for the hole):
  268. // draw a circle instead (with no hole), with the right diameter
  269. TransformCircleToPolygon( aShapeBuffer, center,
  270. outerDiam / 2, seg_per_circle );
  271. }
  272. else
  273. TransformRingToPolygon( aShapeBuffer, center,
  274. (outerDiam - penThickness) / 2,
  275. seg_per_circle, penThickness );
  276. }
  277. // Draw the cross:
  278. ConvertShapeToPolygon( aParent, polybuffer );
  279. rotation = params[8].GetValue( tool ) * 10.0;
  280. for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
  281. {
  282. // shape rotation:
  283. RotatePoint( &polybuffer[ii], -rotation );
  284. // Move to current position:
  285. polybuffer[ii] += curPos;
  286. polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
  287. }
  288. TO_POLY_SHAPE;
  289. }
  290. break;
  291. case AMP_OUTLINE:
  292. {
  293. /* Outline, Primitive Code 4
  294. * An outline primitive is an area enclosed by an n-point polygon defined by its start point and n
  295. * subsequent points. The outline must be closed, i.e. the last point must be equal to the start
  296. * point. There must be at least one subsequent point (to close the outline).
  297. * The outline of the primitive is actually the contour (see 2.6) that consists of linear segments
  298. * only, so it must conform to all the requirements described for contours.
  299. * Warning: Make no mistake: n is the number of subsequent points, being the number of
  300. * vertices of the outline or one less than the number of coordinate pairs.
  301. */
  302. /* Generated by an aperture macro declaration like:
  303. * "4,1,3,0.0,0.0,0.0,0.5,0.5,0.5,0.5,0.0,-25"
  304. * type(4), exposure, corners count, corner1.x, corner.1y, ..., corner1.x, corner.1y, rotation
  305. * type is not stored in parameters list, so the first parameter is exposure
  306. */
  307. // params[0] is the exposure and params[1] is the corners count after the first corner
  308. int numCorners = (int) params[1].GetValue( tool );
  309. // the shape rotation is the last param of list, after corners
  310. int last_prm = params.size() - 1;
  311. rotation = params[last_prm].GetValue( tool ) * 10.0;
  312. wxPoint pos;
  313. // Read points.
  314. // Note: numCorners is the polygon corner count, following the first corner
  315. // * the polygon is always closed,
  316. // * therefore the last XY coordinate is the same as the first
  317. int prm_idx = 2; // params[2] is the first X coordinate
  318. for( int i = 0; i <= numCorners; ++i )
  319. {
  320. pos.x = scaletoIU( params[prm_idx].GetValue( tool ), m_GerbMetric );
  321. prm_idx++;
  322. pos.y = scaletoIU( params[prm_idx].GetValue( tool ), m_GerbMetric );
  323. prm_idx++;
  324. polybuffer.push_back(pos);
  325. // Guard: ensure prm_idx < last_prm
  326. // I saw malformed gerber files with numCorners = number
  327. // of coordinates instead of number of coordinates following the first point
  328. if( prm_idx >= last_prm )
  329. break;
  330. }
  331. // rotate polygon and move it to the actual position
  332. // shape rotation:
  333. for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
  334. {
  335. RotatePoint( &polybuffer[ii], -rotation );
  336. }
  337. // Move to current position:
  338. for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
  339. {
  340. polybuffer[ii] += curPos;
  341. polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
  342. }
  343. TO_POLY_SHAPE;
  344. }
  345. break;
  346. case AMP_POLYGON:
  347. /* Polygon, Primitive Code 5
  348. * A polygon primitive is a regular polygon defined by the number of vertices n, the center point
  349. * and the diameter of the circumscribed circle
  350. */
  351. /* Generated by an aperture macro declaration like:
  352. * "5,1,0.6,0,0,0.5,25"
  353. * type(5), exposure, vertices count, pox.x, pos.y, diameter, rotation
  354. * type is not stored in parameters list, so the first parameter is exposure
  355. */
  356. curPos += mapPt( params[2].GetValue( tool ), params[3].GetValue( tool ), m_GerbMetric );
  357. // Creates the shape:
  358. ConvertShapeToPolygon( aParent, polybuffer );
  359. // rotate polygon and move it to the actual position
  360. rotation = params[5].GetValue( tool ) * 10.0;
  361. for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
  362. {
  363. RotatePoint( &polybuffer[ii], -rotation );
  364. polybuffer[ii] += curPos;
  365. polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
  366. }
  367. TO_POLY_SHAPE;
  368. break;
  369. case AMP_EOF:
  370. // not yet supported, waiting for you.
  371. break;
  372. case AMP_UNKNOWN:
  373. default:
  374. DBG( printf( "AM_PRIMITIVE::DrawBasicShape() err: unknown prim id %d\n",primitive_id) );
  375. break;
  376. }
  377. }
  378. /**
  379. * Function ConvertShapeToPolygon (virtual)
  380. * convert a shape to an equivalent polygon.
  381. * Arcs and circles are approximated by segments
  382. * Useful when a shape is not a graphic primitive (shape with hole,
  383. * rotated shape ... ) and cannot be easily drawn.
  384. * note for some schapes conbining circles and solid lines (rectangles), only rectangles are converted
  385. * because circles are very easy to draw (no rotation problem) so convert them in polygons,
  386. * and draw them as polygons is not a good idea.
  387. */
  388. void AM_PRIMITIVE::ConvertShapeToPolygon( const GERBER_DRAW_ITEM* aParent,
  389. std::vector<wxPoint>& aBuffer )
  390. {
  391. D_CODE* tool = aParent->GetDcodeDescr();
  392. switch( primitive_id )
  393. {
  394. case AMP_CIRCLE:
  395. {
  396. /* Generated by an aperture macro declaration like:
  397. * "1,1,0.3,0.5, 1.0*"
  398. * type (1), exposure, diameter, pos.x, pos.y, <rotation>
  399. * <rotation> is a optional parameter: rotation from origin.
  400. * type is not stored in parameters list, so the first parameter is exposure
  401. */
  402. wxPoint center = mapPt( params[2].GetValue( tool ), params[3].GetValue( tool ), m_GerbMetric );
  403. int radius = scaletoIU( params[1].GetValue( tool ), m_GerbMetric ) / 2;
  404. wxPoint corner;
  405. const int delta = 3600 / seg_per_circle; // rot angle in 0.1 degree
  406. for( int angle = 0; angle < 3600; angle += delta )
  407. {
  408. corner.x = radius;
  409. corner.y = 0;
  410. RotatePoint( &corner, angle );
  411. corner += center;
  412. aBuffer.push_back( corner );
  413. }
  414. }
  415. break;
  416. case AMP_LINE2:
  417. case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation)
  418. {
  419. int width = scaletoIU( params[1].GetValue( tool ), m_GerbMetric );
  420. wxPoint start = mapPt( params[2].GetValue( tool ),
  421. params[3].GetValue( tool ), m_GerbMetric );
  422. wxPoint end = mapPt( params[4].GetValue( tool ),
  423. params[5].GetValue( tool ), m_GerbMetric );
  424. wxPoint delta = end - start;
  425. int len = KiROUND( EuclideanNorm( delta ) );
  426. // To build the polygon, we must create a horizontal polygon starting to "start"
  427. // and rotate it to have the end point to "end"
  428. wxPoint currpt;
  429. currpt.y += width / 2; // Upper left
  430. aBuffer.push_back( currpt );
  431. currpt.x = len; // Upper right
  432. aBuffer.push_back( currpt );
  433. currpt.y -= width; // lower right
  434. aBuffer.push_back( currpt );
  435. currpt.x = 0; // lower left
  436. aBuffer.push_back( currpt );
  437. // Rotate rectangle and move it to the actual start point
  438. double angle = ArcTangente( delta.y, delta.x );
  439. for( unsigned ii = 0; ii < 4; ii++ )
  440. {
  441. RotatePoint( &aBuffer[ii], -angle );
  442. aBuffer[ii] += start;
  443. }
  444. }
  445. break;
  446. case AMP_LINE_CENTER:
  447. {
  448. wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ), m_GerbMetric );
  449. wxPoint pos = mapPt( params[3].GetValue( tool ), params[4].GetValue( tool ), m_GerbMetric );
  450. // Build poly:
  451. pos.x -= size.x / 2;
  452. pos.y -= size.y / 2; // Lower left
  453. aBuffer.push_back( pos );
  454. pos.y += size.y; // Upper left
  455. aBuffer.push_back( pos );
  456. pos.x += size.x; // Upper right
  457. aBuffer.push_back( pos );
  458. pos.y -= size.y; // lower right
  459. aBuffer.push_back( pos );
  460. }
  461. break;
  462. case AMP_LINE_LOWER_LEFT:
  463. {
  464. wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ), m_GerbMetric );
  465. wxPoint lowerLeft = mapPt( params[3].GetValue( tool ), params[4].GetValue(
  466. tool ), m_GerbMetric );
  467. // Build poly:
  468. aBuffer.push_back( lowerLeft );
  469. lowerLeft.y += size.y; // Upper left
  470. aBuffer.push_back( lowerLeft );
  471. lowerLeft.x += size.x; // Upper right
  472. aBuffer.push_back( lowerLeft );
  473. lowerLeft.y -= size.y; // lower right
  474. aBuffer.push_back( lowerLeft );
  475. }
  476. break;
  477. case AMP_THERMAL:
  478. {
  479. // Only 1/4 of the full shape is built, because the other 3 shapes will be draw from this first
  480. // rotated by 90, 180 and 270 deg.
  481. // params = center.x (unused here), center.y (unused here), outside diam, inside diam, crosshair thickness
  482. int outerRadius = scaletoIU( params[2].GetValue( tool ), m_GerbMetric ) / 2;
  483. int innerRadius = scaletoIU( params[3].GetValue( tool ), m_GerbMetric ) / 2;
  484. int halfthickness = scaletoIU( params[4].GetValue( tool ), m_GerbMetric ) / 2;
  485. double angle_start = RAD2DECIDEG( asin( (double) halfthickness / innerRadius ) );
  486. // Draw shape in the first cadrant (X and Y > 0)
  487. wxPoint pos, startpos;
  488. // Inner arc
  489. startpos.x = innerRadius;
  490. double angle_end = 900 - angle_start;
  491. for( double angle = angle_start; angle < angle_end; angle += 100 )
  492. {
  493. pos = startpos;
  494. RotatePoint( &pos, angle );
  495. aBuffer.push_back( pos );
  496. }
  497. // Last point
  498. pos = startpos;
  499. RotatePoint( &pos, angle_end );
  500. aBuffer.push_back( pos );
  501. // outer arc
  502. startpos.x = outerRadius;
  503. startpos.y = 0;
  504. angle_start = RAD2DECIDEG( asin( (double) halfthickness / outerRadius ) );
  505. angle_end = 900 - angle_start;
  506. // First point, near Y axis, outer arc
  507. for( double angle = angle_end; angle > angle_start; angle -= 100 )
  508. {
  509. pos = startpos;
  510. RotatePoint( &pos, angle );
  511. aBuffer.push_back( pos );
  512. }
  513. // last point
  514. pos = startpos;
  515. RotatePoint( &pos, angle_start );
  516. aBuffer.push_back( pos );
  517. aBuffer.push_back( aBuffer[0] ); // Close poly
  518. }
  519. break;
  520. case AMP_MOIRE: // A cross hair with n concentric circles. Only the cros is build as polygon
  521. // because circles can be drawn easily
  522. {
  523. int crossHairThickness = scaletoIU( params[6].GetValue( tool ), m_GerbMetric );
  524. int crossHairLength = scaletoIU( params[7].GetValue( tool ), m_GerbMetric );
  525. // Create cross. First create 1/4 of the shape.
  526. // Others point are the same, totated by 90, 180 and 270 deg
  527. wxPoint pos( crossHairThickness / 2, crossHairLength / 2 );
  528. aBuffer.push_back( pos );
  529. pos.y = crossHairThickness / 2;
  530. aBuffer.push_back( pos );
  531. pos.x = -crossHairLength / 2;
  532. aBuffer.push_back( pos );
  533. pos.y = -crossHairThickness / 2;
  534. aBuffer.push_back( pos );
  535. // Copy the 4 shape, rotated by 90, 180 and 270 deg
  536. for( int jj = 1; jj <= 3; jj ++ )
  537. {
  538. for( int ii = 0; ii < 4; ii++ )
  539. {
  540. pos = aBuffer[ii];
  541. RotatePoint( &pos, jj*900 );
  542. aBuffer.push_back( pos );
  543. }
  544. }
  545. }
  546. break;
  547. case AMP_OUTLINE:
  548. // already is a polygon. Do nothing
  549. break;
  550. case AMP_POLYGON: // Creates a regular polygon
  551. {
  552. int vertexcount = KiROUND( params[1].GetValue( tool ) );
  553. int radius = scaletoIU( params[4].GetValue( tool ), m_GerbMetric ) / 2;
  554. // rs274x said: vertex count = 3 ... 10, and the first corner is on the X axis
  555. if( vertexcount < 3 )
  556. vertexcount = 3;
  557. if( vertexcount > 10 )
  558. vertexcount = 10;
  559. for( int ii = 0; ii <= vertexcount; ii++ )
  560. {
  561. wxPoint pos( radius, 0);
  562. RotatePoint( &pos, ii * 3600 / vertexcount );
  563. aBuffer.push_back( pos );
  564. }
  565. }
  566. break;
  567. case AMP_COMMENT:
  568. case AMP_UNKNOWN:
  569. case AMP_EOF:
  570. break;
  571. }
  572. }
  573. /** GetShapeDim
  574. * Calculate a value that can be used to evaluate the size of text
  575. * when displaying the D-Code of an item
  576. * due to the complexity of the shape of some primitives
  577. * one cannot calculate the "size" of a shape (only abounding box)
  578. * but here, the "dimension" of the shape is the diameter of the primitive
  579. * or for lines the width of the line
  580. * @param aParent = the parent GERBER_DRAW_ITEM which is actually drawn
  581. * @return a dimension, or -1 if no dim to calculate
  582. */
  583. int AM_PRIMITIVE::GetShapeDim( const GERBER_DRAW_ITEM* aParent )
  584. {
  585. int dim = -1;
  586. D_CODE* tool = aParent->GetDcodeDescr();
  587. switch( primitive_id )
  588. {
  589. case AMP_CIRCLE:
  590. // params = exposure, diameter, pos.x, pos.y
  591. dim = scaletoIU( params[1].GetValue( tool ), m_GerbMetric ); // Diameter
  592. break;
  593. case AMP_LINE2:
  594. case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation)
  595. dim = scaletoIU( params[1].GetValue( tool ), m_GerbMetric ); // linne width
  596. break;
  597. case AMP_LINE_CENTER:
  598. {
  599. wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ), m_GerbMetric );
  600. dim = std::min(size.x, size.y);
  601. }
  602. break;
  603. case AMP_LINE_LOWER_LEFT:
  604. {
  605. wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ), m_GerbMetric );
  606. dim = std::min(size.x, size.y);
  607. }
  608. break;
  609. case AMP_THERMAL:
  610. {
  611. // Only 1/4 of the full shape is built, because the other 3 shapes will be draw from this first
  612. // rotated by 90, 180 and 270 deg.
  613. // params = center.x (unused here), center.y (unused here), outside diam, inside diam, crosshair thickness
  614. dim = scaletoIU( params[2].GetValue( tool ), m_GerbMetric ) / 2; // Outer diam
  615. }
  616. break;
  617. case AMP_MOIRE: // A cross hair with n concentric circles.
  618. dim = scaletoIU( params[7].GetValue( tool ), m_GerbMetric ); // = cross hair len
  619. break;
  620. case AMP_OUTLINE: // a free polygon :
  621. // dim = min side of the bounding box (this is a poor criteria, but what is a good criteria b?)
  622. {
  623. // exposure, corners count, corner1.x, corner.1y, ..., rotation
  624. // note: corners count is the count of corners following corner1
  625. int numPoints = (int) params[1].GetValue( tool );
  626. // Read points. numPoints does not include the starting point, so add 1.
  627. // and calculate the bounding box;
  628. wxSize pos_min, pos_max, pos;
  629. int prm_idx = 2; // params[2] is the first X coordinate
  630. int last_prm = params.size() - 1;
  631. for( int i = 0; i<= numPoints; ++i )
  632. {
  633. pos.x = scaletoIU( params[prm_idx].GetValue( tool ), m_GerbMetric );
  634. prm_idx++;
  635. pos.y = scaletoIU( params[prm_idx].GetValue( tool ), m_GerbMetric );
  636. prm_idx++;
  637. if( i == 0 )
  638. pos_min = pos_max = pos;
  639. else
  640. {
  641. // upper right corner:
  642. if( pos_min.x > pos.x )
  643. pos_min.x = pos.x;
  644. if( pos_min.y > pos.y )
  645. pos_min.y = pos.y;
  646. // lower left corner:
  647. if( pos_max.x < pos.x )
  648. pos_max.x = pos.x;
  649. if( pos_max.y < pos.y )
  650. pos_max.y = pos.y;
  651. }
  652. // Guard: ensure prm_idx < last_prm (last prm is orientation)
  653. // I saw malformed gerber files with numCorners = number
  654. // of coordinates instead of number of coordinates following the first point
  655. if( prm_idx >= last_prm )
  656. break;
  657. }
  658. // calculate dim
  659. wxSize size;
  660. size.x = pos_max.x - pos_min.x;
  661. size.y = pos_max.y - pos_min.y;
  662. dim = std::min( size.x, size.y );
  663. }
  664. break;
  665. case AMP_POLYGON: // Regular polygon
  666. dim = scaletoIU( params[4].GetValue( tool ), m_GerbMetric ) / 2; // Radius
  667. break;
  668. case AMP_COMMENT:
  669. case AMP_UNKNOWN:
  670. case AMP_EOF:
  671. break;
  672. }
  673. return dim;
  674. }
  675. SHAPE_POLY_SET* APERTURE_MACRO::GetApertureMacroShape( const GERBER_DRAW_ITEM* aParent,
  676. wxPoint aShapePos )
  677. {
  678. SHAPE_POLY_SET holeBuffer;
  679. bool hasHole = false;
  680. m_shape.RemoveAllContours();
  681. for( AM_PRIMITIVES::iterator prim_macro = primitives.begin();
  682. prim_macro != primitives.end(); ++prim_macro )
  683. {
  684. if( prim_macro->primitive_id == AMP_COMMENT )
  685. continue;
  686. if( prim_macro->IsAMPrimitiveExposureOn( aParent ) )
  687. prim_macro->DrawBasicShape( aParent, m_shape, aShapePos );
  688. else
  689. {
  690. prim_macro->DrawBasicShape( aParent, holeBuffer, aShapePos );
  691. if( holeBuffer.OutlineCount() ) // we have a new hole in shape: remove the hole
  692. {
  693. m_shape.BooleanSubtract( holeBuffer, SHAPE_POLY_SET::PM_FAST );
  694. holeBuffer.RemoveAllContours();
  695. hasHole = true;
  696. }
  697. }
  698. }
  699. // If a hole is defined inside a polygon, we must fracture the polygon
  700. // to be able to drawn it (i.e link holes by overlapping edges)
  701. if( hasHole )
  702. m_shape.Fracture( SHAPE_POLY_SET::PM_FAST );
  703. m_boundingBox = EDA_RECT( wxPoint( 0, 0 ), wxSize( 1, 1 ) );
  704. auto bb = m_shape.BBox();
  705. wxPoint center( bb.Centre().x, bb.Centre().y );
  706. m_boundingBox.Move( aParent->GetABPosition( center ) );
  707. m_boundingBox.Inflate( bb.GetWidth() / 2, bb.GetHeight() / 2 );
  708. return &m_shape;
  709. }
  710. /*
  711. * Function DrawApertureMacroShape
  712. * Draw the primitive shape for flashed items.
  713. * When an item is flashed, this is the shape of the item
  714. */
  715. void APERTURE_MACRO::DrawApertureMacroShape( GERBER_DRAW_ITEM* aParent,
  716. EDA_RECT* aClipBox, wxDC* aDC,
  717. COLOR4D aColor,
  718. wxPoint aShapePos, bool aFilledShape )
  719. {
  720. SHAPE_POLY_SET* shapeBuffer = GetApertureMacroShape( aParent, aShapePos );
  721. if( shapeBuffer->OutlineCount() == 0 )
  722. return;
  723. for( int ii = 0; ii < shapeBuffer->OutlineCount(); ii++ )
  724. {
  725. SHAPE_LINE_CHAIN& poly = shapeBuffer->Outline( ii );
  726. GRClosedPoly( aClipBox, aDC,
  727. poly.PointCount(), (wxPoint*)&poly.Point( 0 ), aFilledShape, aColor, aColor );
  728. }
  729. }
  730. /** GetShapeDim
  731. * Calculate a value that can be used to evaluate the size of text
  732. * when displaying the D-Code of an item
  733. * due to the complexity of a shape using many primitives
  734. * one cannot calculate the "size" of a shape (only abounding box)
  735. * but most of aperture macro are using one or few primitives
  736. * and the "dimension" of the shape is the diameter of the primitive
  737. * (or the max diameter of primitives)
  738. * @return a dimension, or -1 if no dim to calculate
  739. */
  740. int APERTURE_MACRO::GetShapeDim( GERBER_DRAW_ITEM* aParent )
  741. {
  742. int dim = -1;
  743. for( AM_PRIMITIVES::iterator prim_macro = primitives.begin();
  744. prim_macro != primitives.end(); ++prim_macro )
  745. {
  746. int pdim = prim_macro->GetShapeDim( aParent );
  747. if( dim < pdim )
  748. dim = pdim;
  749. }
  750. return dim;
  751. }
  752. /**
  753. * function GetLocalParam
  754. * Usually, parameters are defined inside the aperture primitive
  755. * using immediate mode or defered mode.
  756. * in defered mode the value is defined in a DCODE that want to use the aperture macro.
  757. * But some parameters are defined outside the aperture primitive
  758. * and are local to the aperture macro
  759. * @return the value of a defered parameter defined inside the aperture macro
  760. * @param aParamId = the param id (defined by $3 or $5 ..) to evaluate
  761. */
  762. double APERTURE_MACRO::GetLocalParam( const D_CODE* aDcode, unsigned aParamId ) const
  763. {
  764. // find parameter descr.
  765. const AM_PARAM * param = NULL;
  766. for( unsigned ii = 0; ii < m_localparamStack.size(); ii ++ )
  767. {
  768. if( m_localparamStack[ii].GetIndex() == aParamId )
  769. {
  770. param = &m_localparamStack[ii];
  771. break;
  772. }
  773. }
  774. if ( param == NULL ) // not found
  775. return 0.0;
  776. // Evaluate parameter
  777. double value = param->GetValue( aDcode );
  778. return value;
  779. }