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.

647 lines
23 KiB

  1. /**
  2. * @file am_primitive.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 The 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_basic_shapes_to_polygon.h>
  30. #include <math/util.h> // for KiROUND
  31. #include <gerbview.h>
  32. #include <gerber_file_image.h>
  33. /**
  34. * Convert a distance given in floating point to our internal units.
  35. */
  36. extern int scaletoIU( double aCoord, bool isMetric );
  37. /**
  38. * Translate a point from the aperture macro coordinate system to our
  39. * deci-mils coordinate system.
  40. *
  41. * @return The GerbView coordinate system vector.
  42. */
  43. static VECTOR2I mapPt( double x, double y, bool isMetric )
  44. {
  45. VECTOR2I ret( scaletoIU( x, isMetric ), scaletoIU( y, isMetric ) );
  46. return ret;
  47. }
  48. bool AM_PRIMITIVE::IsAMPrimitiveExposureOn( APERTURE_MACRO* aApertMacro ) const
  49. {
  50. /*
  51. * Some but not all primitives use the first parameter as an exposure control.
  52. * Others are always ON.
  53. * In a aperture macro shape, a basic primitive with exposure off is a hole in the shape
  54. * it is NOT a negative shape
  55. */
  56. wxASSERT( m_Params.size() );
  57. switch( m_Primitive_id )
  58. {
  59. case AMP_CIRCLE:
  60. case AMP_LINE2:
  61. case AMP_LINE20:
  62. case AMP_LINE_CENTER:
  63. case AMP_LINE_LOWER_LEFT:
  64. case AMP_OUTLINE:
  65. case AMP_POLYGON:
  66. // All have an exposure parameter and can return a value (0 or 1)
  67. return m_Params[0].GetValueFromMacro( aApertMacro ) != 0;
  68. break;
  69. case AMP_THERMAL: // Exposure is always on
  70. case AMP_MOIRE: // Exposure is always on
  71. case AMP_UNKNOWN:
  72. default:
  73. return 1; // All have no exposure parameter and are always 0N return true
  74. break;
  75. }
  76. }
  77. void AM_PRIMITIVE::ConvertBasicShapeToPolygon( APERTURE_MACRO* aApertMacro,
  78. SHAPE_POLY_SET& aShapeBuffer )
  79. {
  80. // Draw the primitive shape for flashed items.
  81. // Note: rotation of primitives inside a macro must be always done around the macro origin.
  82. // Create a static buffer to avoid a lot of memory reallocation.
  83. static std::vector<VECTOR2I> polybuffer;
  84. polybuffer.clear();
  85. aApertMacro->EvalLocalParams( *this );
  86. switch( m_Primitive_id )
  87. {
  88. case AMP_CIRCLE: // Circle, given diameter and position
  89. {
  90. /* Generated by an aperture macro declaration like:
  91. * "1,1,0.3,0.5, 1.0*"
  92. * type (1), exposure, diameter, pos.x, pos.y, <rotation>
  93. * <rotation> is a optional parameter: rotation from origin.
  94. * type is not stored in parameters list, so the first parameter is exposure
  95. */
  96. ConvertShapeToPolygon( aApertMacro, polybuffer );
  97. // shape rotation (if any):
  98. if( m_Params.size() >= 5 )
  99. {
  100. EDA_ANGLE rotation( m_Params[4].GetValueFromMacro( aApertMacro ), DEGREES_T );
  101. if( !rotation.IsZero() )
  102. {
  103. for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
  104. RotatePoint( polybuffer[ii], -rotation );
  105. }
  106. }
  107. break;
  108. }
  109. case AMP_LINE2:
  110. case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation)
  111. {
  112. /* Vector Line, Primitive Code 20.
  113. * A vector line is a rectangle defined by its line width, start and end points.
  114. * The line ends are rectangular.
  115. */
  116. /* Generated by an aperture macro declaration like:
  117. * "2,1,0.3,0,0, 0.5, 1.0,-135*"
  118. * type (2), exposure, width, start.x, start.y, end.x, end.y, rotation
  119. * type is not stored in parameters list, so the first parameter is exposure
  120. */
  121. ConvertShapeToPolygon( aApertMacro, polybuffer );
  122. // shape rotation:
  123. EDA_ANGLE rotation( m_Params[6].GetValueFromMacro( aApertMacro ), DEGREES_T );
  124. if( !rotation.IsZero() )
  125. {
  126. for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
  127. RotatePoint( polybuffer[ii], -rotation );
  128. }
  129. break;
  130. }
  131. case AMP_LINE_CENTER:
  132. {
  133. /* Center Line, Primitive Code 21
  134. * A center line primitive is a rectangle defined by its width, height, and center point
  135. */
  136. /* Generated by an aperture macro declaration like:
  137. * "21,1,0.3,0.03,0,0,-135*"
  138. * type (21), exposure, ,width, height, center pos.x, center pos.y, rotation
  139. * type is not stored in parameters list, so the first parameter is exposure
  140. */
  141. ConvertShapeToPolygon( aApertMacro, polybuffer );
  142. // shape rotation:
  143. EDA_ANGLE rotation( m_Params[5].GetValueFromMacro( aApertMacro ), DEGREES_T );
  144. if( !rotation.IsZero() )
  145. {
  146. for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
  147. RotatePoint( polybuffer[ii], -rotation );
  148. }
  149. break;
  150. }
  151. case AMP_LINE_LOWER_LEFT:
  152. {
  153. /* Generated by an aperture macro declaration like:
  154. * "22,1,0.3,0.03,0,0,-135*"
  155. * type (22), exposure, ,width, height, corner pos.x, corner pos.y, rotation
  156. * type is not stored in parameters list, so the first parameter is exposure
  157. */
  158. ConvertShapeToPolygon( aApertMacro, polybuffer );
  159. // shape rotation:
  160. EDA_ANGLE rotation( m_Params[5].GetValueFromMacro( aApertMacro ), DEGREES_T );
  161. if( !rotation.IsZero() )
  162. {
  163. for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
  164. RotatePoint( polybuffer[ii], -rotation );
  165. }
  166. break;
  167. }
  168. case AMP_THERMAL:
  169. {
  170. /* Generated by an aperture macro declaration like:
  171. * "7, 0,0,1.0,0.3,0.01,-13*"
  172. * type (7), center.x , center.y, outside diam, inside diam, crosshair thickness, rotation
  173. * type is not stored in parameters list, so the first parameter is center.x
  174. *
  175. * The thermal primitive is a ring (annulus) interrupted by four gaps. Exposure is always
  176. * on.
  177. */
  178. std::vector<VECTOR2I> subshape_poly;
  179. VECTOR2I center( mapPt( m_Params[0].GetValueFromMacro( aApertMacro ),
  180. m_Params[1].GetValueFromMacro( aApertMacro ), m_GerbMetric ) );
  181. ConvertShapeToPolygon( aApertMacro, subshape_poly );
  182. // shape rotation:
  183. EDA_ANGLE rotation( m_Params[5].GetValueFromMacro( aApertMacro ), DEGREES_T );
  184. // Because a thermal shape has 4 identical sub-shapes, only one is created in subshape_poly.
  185. // We must draw 4 sub-shapes rotated by 90 deg
  186. for( int ii = 0; ii < 4; ii++ )
  187. {
  188. polybuffer = subshape_poly;
  189. EDA_ANGLE sub_rotation = ANGLE_90 * ii;
  190. for( unsigned jj = 0; jj < polybuffer.size(); jj++ )
  191. RotatePoint( polybuffer[jj], -sub_rotation );
  192. // Move to center position given by the tool, and rotate the full shape around
  193. // the center position (origin of the macro):
  194. for( unsigned jj = 0; jj < polybuffer.size(); jj++ )
  195. {
  196. polybuffer[jj] += center;
  197. RotatePoint( polybuffer[jj], -rotation );
  198. }
  199. aShapeBuffer.NewOutline();
  200. for( unsigned jj = 0; jj < polybuffer.size(); jj++ )
  201. aShapeBuffer.Append( polybuffer[jj] );
  202. aShapeBuffer.Append( polybuffer[0] );
  203. }
  204. }
  205. break;
  206. case AMP_MOIRE:
  207. {
  208. /* Moire, Primitive Code 6
  209. * The moire primitive is a cross hair centered on concentric rings (annuli).
  210. * Exposure is always on.
  211. */
  212. /* Generated by an aperture macro declaration like:
  213. * "6,0,0,0.125,.01,0.01,3,0.003,0.150,0"
  214. * type(6), pos.x, pos.y, diam, penwidth, gap, circlecount, crosshair thickness,
  215. * crosshair len, rotation. The type is not stored in parameters list, so the first
  216. * parameter is pos.x.
  217. */
  218. int outerDiam = scaletoIU( m_Params[2].GetValueFromMacro( aApertMacro ), m_GerbMetric );
  219. int penThickness = scaletoIU( m_Params[3].GetValueFromMacro( aApertMacro ), m_GerbMetric );
  220. int gap = scaletoIU( m_Params[4].GetValueFromMacro( aApertMacro ), m_GerbMetric );
  221. int numCircles = KiROUND( m_Params[5].GetValueFromMacro( aApertMacro ) );
  222. // Adjust the allowed approx error to convert arcs to segments:
  223. int arc_to_seg_error = gerbIUScale.mmToIU( 0.005 ); // Allow 5 microns
  224. // Draw circles @ position pos.x, pos.y given by the tool:
  225. VECTOR2I center( mapPt( m_Params[0].GetValueFromMacro( aApertMacro ),
  226. m_Params[1].GetValueFromMacro( aApertMacro ), m_GerbMetric ) );
  227. EDA_ANGLE rotation( m_Params[8].GetValueFromMacro( aApertMacro ), DEGREES_T );
  228. // adjust outerDiam by this on each nested circle
  229. int diamAdjust = ( gap + penThickness ) * 2;
  230. for( int i = 0; i < numCircles; ++i, outerDiam -= diamAdjust )
  231. {
  232. if( outerDiam <= 0 )
  233. break;
  234. // calculate the rotated position of the center:
  235. VECTOR2I circle_center = center;
  236. RotatePoint( circle_center, -rotation );
  237. // Note: outerDiam is the outer diameter of the ring.
  238. // the ring graphic diameter is (outerDiam - penThickness)
  239. if( outerDiam <= penThickness )
  240. { // No room to draw a ring (no room for the hole):
  241. // draw a circle instead (with no hole), with the right diameter
  242. TransformCircleToPolygon( aShapeBuffer, circle_center, outerDiam / 2, arc_to_seg_error,
  243. ERROR_INSIDE );
  244. }
  245. else
  246. {
  247. TransformRingToPolygon( aShapeBuffer, circle_center, ( outerDiam - penThickness ) / 2,
  248. penThickness, arc_to_seg_error, ERROR_INSIDE );
  249. }
  250. }
  251. // Draw the cross:
  252. ConvertShapeToPolygon( aApertMacro, polybuffer );
  253. for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
  254. {
  255. // move crossair shape to center and rotate shape:
  256. polybuffer[ii] += center;
  257. RotatePoint( polybuffer[ii], -rotation );
  258. }
  259. break;
  260. }
  261. case AMP_OUTLINE:
  262. {
  263. /* Outline, Primitive Code 4
  264. * An outline primitive is an area enclosed by an n-point polygon defined by its start
  265. * point and n
  266. * subsequent points. The outline must be closed, i.e. the last point must be equal to
  267. * the start point. There must be at least one subsequent point (to close the outline).
  268. * The outline of the primitive is actually the contour (see 2.6) that consists of linear
  269. * segments only, so it must conform to all the requirements described for contours.
  270. * Warning: Make no mistake: n is the number of subsequent points, being the number of
  271. * vertices of the outline or one less than the number of coordinate pairs.
  272. */
  273. /* Generated by an aperture macro declaration like:
  274. * "4,1,3,0.0,0.0,0.0,0.5,0.5,0.5,0.5,0.0,-25"
  275. * type(4), exposure, corners count, corner1.x, corner.1y, ..., corner1.x, corner.1y,
  276. * rotation
  277. * type is not stored in parameters list, so the first parameter is exposure
  278. */
  279. // m_Params[0] is the exposure and m_Params[1] is the corners count after the first corner
  280. int numCorners = (int) m_Params[1].GetValueFromMacro( aApertMacro );
  281. // the shape rotation is the last param of list, after corners
  282. int last_prm = m_Params.size() - 1;
  283. EDA_ANGLE rotation( m_Params[last_prm].GetValueFromMacro( aApertMacro ), DEGREES_T );
  284. VECTOR2I pos;
  285. // Read points.
  286. // Note: numCorners is the polygon corner count, following the first corner
  287. // * the polygon is always closed,
  288. // * therefore the last XY coordinate is the same as the first
  289. int prm_idx = 2; // m_Params[2] is the first X coordinate
  290. for( int i = 0; i <= numCorners; ++i )
  291. {
  292. pos.x = scaletoIU( m_Params[prm_idx].GetValueFromMacro( aApertMacro ), m_GerbMetric );
  293. prm_idx++;
  294. pos.y = scaletoIU( m_Params[prm_idx].GetValueFromMacro( aApertMacro ), m_GerbMetric );
  295. prm_idx++;
  296. polybuffer.push_back(pos);
  297. // Guard: ensure prm_idx < last_prm
  298. // I saw malformed gerber files with numCorners = number
  299. // of coordinates instead of number of coordinates following the first point
  300. if( prm_idx >= last_prm )
  301. break;
  302. }
  303. // rotate polygon and move it to the actual position shape rotation:
  304. for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
  305. {
  306. RotatePoint( polybuffer[ii], -rotation );
  307. }
  308. break;
  309. }
  310. case AMP_POLYGON:
  311. {
  312. /* Polygon, Primitive Code 5
  313. * A polygon primitive is a regular polygon defined by the number of vertices n, the
  314. * center point and the diameter of the circumscribed circle
  315. */
  316. /* Generated by an aperture macro declaration like:
  317. * "5,1,0.6,0,0,0.5,25"
  318. * type(5), exposure, vertices count, pox.x, pos.y, diameter, rotation
  319. * type is not stored in parameters list, so the first parameter is exposure
  320. */
  321. VECTOR2I curPos( mapPt( m_Params[2].GetValueFromMacro( aApertMacro ),
  322. m_Params[3].GetValueFromMacro( aApertMacro ), m_GerbMetric ) );
  323. // Creates the shape:
  324. ConvertShapeToPolygon( aApertMacro, polybuffer );
  325. // move and rotate polygonal shape
  326. EDA_ANGLE rotation( m_Params[5].GetValueFromMacro( aApertMacro ), DEGREES_T );
  327. for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
  328. {
  329. polybuffer[ii] += curPos;
  330. RotatePoint( polybuffer[ii], -rotation );
  331. }
  332. break;
  333. }
  334. case AMP_COMMENT:
  335. case AMP_UNKNOWN:
  336. break;
  337. }
  338. if( polybuffer.size() > 1 ) // a valid polygon has more than 1 corner
  339. {
  340. aShapeBuffer.NewOutline();
  341. for( unsigned jj = 0; jj < polybuffer.size(); jj++ )
  342. aShapeBuffer.Append( polybuffer[jj] );
  343. // Close the shape:
  344. aShapeBuffer.Append( polybuffer[0] );
  345. }
  346. }
  347. void AM_PRIMITIVE::ConvertShapeToPolygon( APERTURE_MACRO* aApertMacro,
  348. std::vector<VECTOR2I>& aBuffer )
  349. {
  350. switch( m_Primitive_id )
  351. {
  352. case AMP_CIRCLE:
  353. {
  354. /* Generated by an aperture macro declaration like:
  355. * "1,1,0.3,0.5, 1.0*"
  356. * type (1), exposure, diameter, pos.x, pos.y, <rotation>
  357. * <rotation> is a optional parameter: rotation from origin.
  358. * type is not stored in parameters list, so the first parameter is exposure
  359. */
  360. int radius = scaletoIU( m_Params[1].GetValueFromMacro( aApertMacro ), m_GerbMetric ) / 2;
  361. // A circle primitive can have a 0 size (for instance when used in roundrect macro),
  362. // so skip it
  363. if( radius <= 0 )
  364. break;
  365. VECTOR2I center = mapPt( m_Params[2].GetValueFromMacro( aApertMacro ), m_Params[3].GetValueFromMacro( aApertMacro ),
  366. m_GerbMetric );
  367. VECTOR2I corner;
  368. const int seg_per_circle = 64; // Number of segments to approximate a circle
  369. EDA_ANGLE delta = ANGLE_360 / seg_per_circle;
  370. for( EDA_ANGLE angle = ANGLE_0; angle < ANGLE_360; angle += delta )
  371. {
  372. corner.x = radius;
  373. corner.y = 0;
  374. RotatePoint( corner, angle );
  375. corner += center;
  376. aBuffer.push_back( corner );
  377. }
  378. break;
  379. }
  380. case AMP_LINE2:
  381. case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation)
  382. {
  383. int width = scaletoIU( m_Params[1].GetValueFromMacro( aApertMacro ), m_GerbMetric );
  384. VECTOR2I start =
  385. mapPt( m_Params[2].GetValueFromMacro( aApertMacro ), m_Params[3].GetValueFromMacro( aApertMacro ), m_GerbMetric );
  386. VECTOR2I end =
  387. mapPt( m_Params[4].GetValueFromMacro( aApertMacro ), m_Params[5].GetValueFromMacro( aApertMacro ), m_GerbMetric );
  388. VECTOR2I delta = end - start;
  389. int len = delta.EuclideanNorm();
  390. // To build the polygon, we must create a horizontal polygon starting to "start"
  391. // and rotate it to have the end point to "end"
  392. VECTOR2I currpt;
  393. currpt.y += width / 2; // Upper left
  394. aBuffer.push_back( currpt );
  395. currpt.x = len; // Upper right
  396. aBuffer.push_back( currpt );
  397. currpt.y -= width; // lower right
  398. aBuffer.push_back( currpt );
  399. currpt.x = 0; // lower left
  400. aBuffer.push_back( currpt );
  401. // Rotate rectangle and move it to the actual start point
  402. EDA_ANGLE angle( delta );
  403. for( unsigned ii = 0; ii < 4; ii++ )
  404. {
  405. RotatePoint( aBuffer[ii], -angle );
  406. aBuffer[ii] += start;
  407. }
  408. break;
  409. }
  410. case AMP_LINE_CENTER:
  411. {
  412. VECTOR2I size =
  413. mapPt( m_Params[1].GetValueFromMacro( aApertMacro ), m_Params[2].GetValueFromMacro( aApertMacro ), m_GerbMetric );
  414. VECTOR2I pos =
  415. mapPt( m_Params[3].GetValueFromMacro( aApertMacro ), m_Params[4].GetValueFromMacro( aApertMacro ), m_GerbMetric );
  416. // Build poly:
  417. pos.x -= size.x / 2;
  418. pos.y -= size.y / 2; // Lower left
  419. aBuffer.push_back( pos );
  420. pos.y += size.y; // Upper left
  421. aBuffer.push_back( pos );
  422. pos.x += size.x; // Upper right
  423. aBuffer.push_back( pos );
  424. pos.y -= size.y; // lower right
  425. aBuffer.push_back( pos );
  426. break;
  427. }
  428. case AMP_LINE_LOWER_LEFT:
  429. {
  430. VECTOR2I size =
  431. mapPt( m_Params[1].GetValueFromMacro( aApertMacro ), m_Params[2].GetValueFromMacro( aApertMacro ), m_GerbMetric );
  432. VECTOR2I lowerLeft =
  433. mapPt( m_Params[3].GetValueFromMacro( aApertMacro ), m_Params[4].GetValueFromMacro( aApertMacro ), m_GerbMetric );
  434. // Build poly:
  435. aBuffer.push_back( lowerLeft );
  436. lowerLeft.y += size.y; // Upper left
  437. aBuffer.push_back( lowerLeft );
  438. lowerLeft.x += size.x; // Upper right
  439. aBuffer.push_back( lowerLeft );
  440. lowerLeft.y -= size.y; // lower right
  441. aBuffer.push_back( lowerLeft );
  442. break;
  443. }
  444. case AMP_THERMAL:
  445. {
  446. // Only 1/4 of the full shape is built, because the other 3 shapes will be draw from
  447. // this first rotated by 90, 180 and 270 deg.
  448. // m_Params = center.x (unused here), center.y (unused here), outside diam, inside diam,
  449. // crosshair thickness.
  450. int outerRadius = scaletoIU( m_Params[2].GetValueFromMacro( aApertMacro ), m_GerbMetric ) / 2;
  451. int innerRadius = scaletoIU( m_Params[3].GetValueFromMacro( aApertMacro ), m_GerbMetric ) / 2;
  452. // Safety checks to guarantee no divide-by-zero
  453. outerRadius = std::max( 1, outerRadius );
  454. innerRadius = std::max( 1, innerRadius );
  455. int halfthickness = scaletoIU( m_Params[4].GetValueFromMacro( aApertMacro ), m_GerbMetric ) / 2;
  456. EDA_ANGLE angle_start( asin( (double) halfthickness / innerRadius ), RADIANS_T );
  457. // Draw shape in the first quadrant (X and Y > 0)
  458. VECTOR2I pos, startpos;
  459. // Inner arc
  460. startpos.x = innerRadius;
  461. EDA_ANGLE angle_end = ANGLE_90 - angle_start;
  462. for( EDA_ANGLE angle = angle_start; angle < angle_end; angle += EDA_ANGLE( 10, DEGREES_T ) )
  463. {
  464. pos = startpos;
  465. RotatePoint( pos, angle );
  466. aBuffer.push_back( pos );
  467. }
  468. // Last point
  469. pos = startpos;
  470. RotatePoint( pos, angle_end );
  471. aBuffer.push_back( pos );
  472. // outer arc
  473. startpos.x = outerRadius;
  474. startpos.y = 0;
  475. angle_start = EDA_ANGLE( asin( (double) halfthickness / outerRadius ), RADIANS_T );
  476. angle_end = ANGLE_90 - angle_start;
  477. // First point, near Y axis, outer arc
  478. for( EDA_ANGLE angle = angle_end; angle > angle_start; angle -= EDA_ANGLE( 10, DEGREES_T ) )
  479. {
  480. pos = startpos;
  481. RotatePoint( pos, angle );
  482. aBuffer.push_back( pos );
  483. }
  484. // last point
  485. pos = startpos;
  486. RotatePoint( pos, angle_start );
  487. aBuffer.push_back( pos );
  488. aBuffer.push_back( aBuffer[0] ); // Close poly
  489. }
  490. break;
  491. case AMP_MOIRE:
  492. {
  493. // A cross hair with n concentric circles. Only the cross is built as
  494. // polygon because circles can be drawn easily
  495. int crossHairThickness = scaletoIU( m_Params[6].GetValueFromMacro( aApertMacro ), m_GerbMetric );
  496. int crossHairLength = scaletoIU( m_Params[7].GetValueFromMacro( aApertMacro ), m_GerbMetric );
  497. // Create cross. First create 1/4 of the shape.
  498. // Others point are the same, rotated by 90, 180 and 270 deg
  499. VECTOR2I pos( crossHairThickness / 2, crossHairLength / 2 );
  500. aBuffer.push_back( pos );
  501. pos.y = crossHairThickness / 2;
  502. aBuffer.push_back( pos );
  503. pos.x = -crossHairLength / 2;
  504. aBuffer.push_back( pos );
  505. pos.y = -crossHairThickness / 2;
  506. aBuffer.push_back( pos );
  507. // Copy the 4 shape, rotated by 90, 180 and 270 deg
  508. for( int jj = 1; jj <= 3; jj ++ )
  509. {
  510. for( int ii = 0; ii < 4; ii++ )
  511. {
  512. pos = aBuffer[ii];
  513. RotatePoint( pos, ANGLE_90 * jj );
  514. aBuffer.push_back( pos );
  515. }
  516. }
  517. break;
  518. }
  519. case AMP_OUTLINE:
  520. // already is a polygon. Do nothing
  521. break;
  522. case AMP_POLYGON: // Creates a regular polygon
  523. {
  524. int vertexcount = KiROUND( m_Params[1].GetValueFromMacro( aApertMacro ) );
  525. int radius = scaletoIU( m_Params[4].GetValueFromMacro( aApertMacro ), m_GerbMetric ) / 2;
  526. // rs274x said: vertex count = 3 ... 10, and the first corner is on the X axis
  527. if( vertexcount < 3 )
  528. vertexcount = 3;
  529. if( vertexcount > 10 )
  530. vertexcount = 10;
  531. for( int ii = 0; ii <= vertexcount; ii++ )
  532. {
  533. VECTOR2I pos( radius, 0 );
  534. RotatePoint( pos, ANGLE_360 * ii / vertexcount );
  535. aBuffer.push_back( pos );
  536. }
  537. break;
  538. }
  539. case AMP_COMMENT:
  540. case AMP_UNKNOWN:
  541. break;
  542. }
  543. }