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.

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