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.

914 lines
36 KiB

  1. /**********************************************/
  2. /* board_items_to_polygon_shape_transform.cpp */
  3. /**********************************************/
  4. /* Function to convert pads and tranck shapes to polygons
  5. * Used to fill zones areas
  6. */
  7. #include <vector>
  8. #include "fctsys.h"
  9. #include "pcbnew.h"
  10. #include "wxPcbStruct.h"
  11. #include "trigo.h"
  12. #include "macros.h"
  13. #include "class_pad.h"
  14. #include "class_track.h"
  15. #include "class_drawsegment.h"
  16. #include "class_pcb_text.h"
  17. /* Exported functions */
  18. /**
  19. * Function TransformRoundedEndsSegmentToPolygon
  20. * convert a segment with rounded ends to a polygon
  21. * Convert arcs to multiple straight lines
  22. * @param aCornerBuffer = a buffer to store the polygon
  23. * @param aStart = the segment start point coordinate
  24. * @param aEnd = the segment end point coordinate
  25. * @param aCircleToSegmentsCount = the number of segments to approximate a circle
  26. * @param aWidth = the segment width
  27. */
  28. void TransformRoundedEndsSegmentToPolygon( std::vector <CPolyPt>& aCornerBuffer,
  29. wxPoint aStart, wxPoint aEnd,
  30. int aCircleToSegmentsCount,
  31. int aWidth );
  32. /**
  33. * Function TransformArcToPolygon
  34. * Creates a polygon from an Arc
  35. * Convert arcs to multiple straight segments
  36. * @param aCornerBuffer = a buffer to store the polygon
  37. * @param aCentre = centre of the arc or circle
  38. * @param aStart = start point of the arc, or a point on the circle
  39. * @param aArcAngle = arc angle in 0.1 degrees. For a circle, aArcAngle = 3600
  40. * @param aCircleToSegmentsCount = the number of segments to approximate a circle
  41. * @param aWidth = width (thickness) of the line
  42. */
  43. void TransformArcToPolygon( std::vector <CPolyPt>& aCornerBuffer,
  44. wxPoint aCentre, wxPoint aStart, int aArcAngle,
  45. int aCircleToSegmentsCount, int aWidth )
  46. {
  47. wxPoint arc_start, arc_end;
  48. int delta = 3600 / aCircleToSegmentsCount; // rotate angle in 0.1 degree
  49. arc_end = arc_start = aStart;
  50. if( aArcAngle != 3600 )
  51. {
  52. RotatePoint( &arc_end, aCentre, -aArcAngle );
  53. }
  54. if( aArcAngle < 0 )
  55. {
  56. EXCHG( arc_start, arc_end );
  57. NEGATE( aArcAngle );
  58. }
  59. // Compute the ends of segments and creates poly
  60. wxPoint curr_end = arc_start;
  61. wxPoint curr_start = arc_start;
  62. for( int ii = delta; ii < aArcAngle; ii += delta )
  63. {
  64. curr_end = arc_start;
  65. RotatePoint( &curr_end, aCentre, -ii );
  66. TransformRoundedEndsSegmentToPolygon( aCornerBuffer, curr_start, curr_end,
  67. aCircleToSegmentsCount, aWidth );
  68. curr_start = curr_end;
  69. }
  70. if( curr_end != arc_end )
  71. TransformRoundedEndsSegmentToPolygon( aCornerBuffer,
  72. curr_end, arc_end, aCircleToSegmentsCount, aWidth );
  73. }
  74. /**
  75. * Function TransformShapeWithClearanceToPolygon
  76. * Convert the track shape to a closed polygon
  77. * Used in filling zones calculations
  78. * Circles and arcs are approximated by segments
  79. * @param aCornerBuffer = a buffer to store the polygon
  80. * @param aClearanceValue = the clearance around the pad
  81. * @param aCircleToSegmentsCount = the number of segments to approximate a circle
  82. * @param aCorrectionFactor = the correction to apply to circles radius to keep
  83. * clearance when the circle is approximated by segment bigger or equal
  84. * to the real clearance value (usually near from 1.0)
  85. */
  86. void TEXTE_PCB::TransformShapeWithClearanceToPolygon( std::vector <CPolyPt>& aCornerBuffer,
  87. int aClearanceValue,
  88. int aCircleToSegmentsCount,
  89. double aCorrectionFactor )
  90. {
  91. if( GetLength() == 0 )
  92. return;
  93. CPolyPt corners[4]; // Buffer of polygon corners
  94. EDA_RECT rect = GetTextBox( -1 );
  95. rect.Inflate( aClearanceValue );
  96. corners[0].x = rect.GetOrigin().x;
  97. corners[0].y = rect.GetOrigin().y;
  98. corners[1].y = corners[0].y;
  99. corners[1].x = rect.GetRight();
  100. corners[2].x = corners[1].x;
  101. corners[2].y = rect.GetBottom();
  102. corners[3].y = corners[2].y;
  103. corners[3].x = corners[0].x;
  104. for( int ii = 0; ii < 4; ii++ )
  105. {
  106. // Rotate polygon
  107. RotatePoint( &corners[ii].x, &corners[ii].y, m_Pos.x, m_Pos.y, m_Orient );
  108. aCornerBuffer.push_back( corners[ii] );
  109. }
  110. aCornerBuffer.back().end_contour = true;
  111. }
  112. /**
  113. * Function TransformShapeWithClearanceToPolygon
  114. * Convert the track shape to a closed polygon
  115. * Used in filling zones calculations
  116. * Circles and arcs are approximated by segments
  117. * @param aCornerBuffer = a buffer to store the polygon
  118. * @param aClearanceValue = the clearance around the pad
  119. * @param aCircleToSegmentsCount = the number of segments to approximate a circle
  120. * @param aCorrectionFactor = the correction to apply to circles radius to keep
  121. * clearance when the circle is approxiamted by segment bigger or equal
  122. * to the real clearance value (usually near from 1.0)
  123. */
  124. void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( std::vector <CPolyPt>& aCornerBuffer,
  125. int aClearanceValue,
  126. int aCircleToSegmentsCount,
  127. double aCorrectionFactor )
  128. {
  129. switch( m_Shape )
  130. {
  131. case S_CIRCLE:
  132. TransformArcToPolygon( aCornerBuffer, m_Start, // Circle centre
  133. m_End, 3600,
  134. aCircleToSegmentsCount,
  135. m_Width + (2 * aClearanceValue) );
  136. break;
  137. case S_ARC:
  138. TransformArcToPolygon( aCornerBuffer, m_Start,
  139. m_End, m_Angle,
  140. aCircleToSegmentsCount,
  141. m_Width + (2 * aClearanceValue) );
  142. break;
  143. default:
  144. TransformRoundedEndsSegmentToPolygon( aCornerBuffer, m_Start, m_End,
  145. aCircleToSegmentsCount,
  146. m_Width + (2 * aClearanceValue) );
  147. break;
  148. }
  149. }
  150. /**
  151. * Function TransformShapeWithClearanceToPolygon
  152. * Convert the track shape to a closed polygon
  153. * Used in filling zones calculations
  154. * Circles (vias) and arcs (ends of tracks) are approximated by segments
  155. * @param aCornerBuffer = a buffer to store the polygon
  156. * @param aClearanceValue = the clearance around the pad
  157. * @param aCircleToSegmentsCount = the number of segments to approximate a circle
  158. * @param aCorrectionFactor = the correction to apply to circles radius to keep
  159. * clearance when the circle is approxiamted by segment bigger or equal
  160. * to the real clearance value (usually near from 1.0)
  161. */
  162. void TRACK:: TransformShapeWithClearanceToPolygon( std:: vector < CPolyPt>& aCornerBuffer,
  163. int aClearanceValue,
  164. int aCircleToSegmentsCount,
  165. double aCorrectionFactor )
  166. {
  167. wxPoint corner_position;
  168. int ii, angle;
  169. int dx = (m_Width / 2) + aClearanceValue;
  170. int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree
  171. switch( Type() )
  172. {
  173. case PCB_VIA_T:
  174. dx = (int) ( dx * aCorrectionFactor );
  175. for( ii = 0; ii < aCircleToSegmentsCount; ii++ )
  176. {
  177. corner_position = wxPoint( dx, 0 );
  178. RotatePoint( &corner_position.x, &corner_position.y, (1800 / aCircleToSegmentsCount) );
  179. angle = ii * delta;
  180. RotatePoint( &corner_position.x, &corner_position.y, angle );
  181. corner_position.x += m_Start.x;
  182. corner_position.y += m_Start.y;
  183. CPolyPt polypoint( corner_position.x, corner_position.y );
  184. aCornerBuffer.push_back( polypoint );
  185. }
  186. aCornerBuffer.back().end_contour = true;
  187. break;
  188. default:
  189. TransformRoundedEndsSegmentToPolygon( aCornerBuffer,
  190. m_Start, m_End,
  191. aCircleToSegmentsCount,
  192. m_Width + ( 2 * aClearanceValue) );
  193. break;
  194. }
  195. }
  196. /* Function TransformRoundedEndsSegmentToPolygon
  197. */
  198. void TransformRoundedEndsSegmentToPolygon( std::vector <CPolyPt>& aCornerBuffer,
  199. wxPoint aStart, wxPoint aEnd,
  200. int aCircleToSegmentsCount,
  201. int aWidth )
  202. {
  203. int radius = aWidth / 2;
  204. wxPoint endp = aEnd - aStart; // end point coordinate for the same segment starting at (0,0)
  205. wxPoint startp = aStart;
  206. wxPoint corner;
  207. int seg_len;
  208. CPolyPt polypoint;
  209. // normalize the position in order to have endp.x >= 0;
  210. if( endp.x < 0 )
  211. {
  212. endp = aStart - aEnd;
  213. startp = aEnd;
  214. }
  215. int delta_angle = ArcTangente( endp.y, endp.x ); // delta_angle is in 0.1 degrees
  216. seg_len = (int) sqrt( ( (double) endp.y * endp.y ) + ( (double) endp.x * endp.x ) );
  217. int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree
  218. // Compute the outlines of the segment, and creates a polygon
  219. corner = wxPoint( 0, radius );
  220. RotatePoint( &corner, -delta_angle );
  221. corner += startp;
  222. polypoint.x = corner.x;
  223. polypoint.y = corner.y;
  224. aCornerBuffer.push_back( polypoint );
  225. corner = wxPoint( seg_len, radius );
  226. RotatePoint( &corner, -delta_angle );
  227. corner += startp;
  228. polypoint.x = corner.x;
  229. polypoint.y = corner.y;
  230. aCornerBuffer.push_back( polypoint );
  231. // add right rounded end:
  232. for( int ii = delta; ii < 1800; ii += delta )
  233. {
  234. corner = wxPoint( 0, radius );
  235. RotatePoint( &corner, ii );
  236. corner.x += seg_len;
  237. RotatePoint( &corner, -delta_angle );
  238. corner += startp;
  239. polypoint.x = corner.x;
  240. polypoint.y = corner.y;
  241. aCornerBuffer.push_back( polypoint );
  242. }
  243. corner = wxPoint( seg_len, -radius );
  244. RotatePoint( &corner, -delta_angle );
  245. corner += startp;
  246. polypoint.x = corner.x;
  247. polypoint.y = corner.y;
  248. aCornerBuffer.push_back( polypoint );
  249. corner = wxPoint( 0, -radius );
  250. RotatePoint( &corner, -delta_angle );
  251. corner += startp;
  252. polypoint.x = corner.x;
  253. polypoint.y = corner.y;
  254. aCornerBuffer.push_back( polypoint );
  255. // add left rounded end:
  256. for( int ii = delta; ii < 1800; ii += delta )
  257. {
  258. corner = wxPoint( 0, -radius );
  259. RotatePoint( &corner, ii );
  260. RotatePoint( &corner, -delta_angle );
  261. corner += startp;
  262. polypoint.x = corner.x;
  263. polypoint.y = corner.y;
  264. aCornerBuffer.push_back( polypoint );
  265. }
  266. aCornerBuffer.back().end_contour = true;
  267. }
  268. /**
  269. * Function TransformShapeWithClearanceToPolygon
  270. * Convert the pad shape to a closed polygon
  271. * Used in filling zones calculations
  272. * Circles and arcs are approximated by segments
  273. * @param aCornerBuffer = a buffer to store the polygon
  274. * @param aClearanceValue = the clearance around the pad
  275. * @param aCircleToSegmentsCount = the number of segments to approximate a circle
  276. * @param aCorrectionFactor = the correction to apply to circles radius to keep
  277. * clearance when the circle is approxiamted by segment bigger or equal
  278. * to the real clearance value (usually near from 1.0)
  279. */
  280. void D_PAD:: TransformShapeWithClearanceToPolygon( std:: vector < CPolyPt>& aCornerBuffer,
  281. int aClearanceValue,
  282. int aCircleToSegmentsCount,
  283. double aCorrectionFactor )
  284. {
  285. wxPoint corner_position;
  286. int ii, angle;
  287. int dx = (m_Size.x / 2) + aClearanceValue;
  288. int dy = (m_Size.y / 2) + aClearanceValue;
  289. int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree
  290. wxPoint PadShapePos = ReturnShapePos(); /* Note: for pad having a shape offset,
  291. * the pad position is NOT the shape position */
  292. wxSize psize = m_Size; /* pad size unsed in RECT and TRAPEZOIDAL pads
  293. * trapezoidal pads are considered as rect
  294. * pad shape having they boudary box size */
  295. switch( m_PadShape )
  296. {
  297. case PAD_CIRCLE:
  298. dx = (int) ( dx * aCorrectionFactor );
  299. for( ii = 0; ii < aCircleToSegmentsCount; ii++ )
  300. {
  301. corner_position = wxPoint( dx, 0 );
  302. RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) );
  303. // Half increment offset to get more space between
  304. angle = ii * delta;
  305. RotatePoint( &corner_position, angle );
  306. corner_position += PadShapePos;
  307. CPolyPt polypoint( corner_position.x, corner_position.y );
  308. aCornerBuffer.push_back( polypoint );
  309. }
  310. aCornerBuffer.back().end_contour = true;
  311. break;
  312. case PAD_OVAL:
  313. angle = m_Orient;
  314. if( dy > dx ) // Oval pad X/Y ratio for choosing translation axles
  315. {
  316. dy = (int) ( dy * aCorrectionFactor );
  317. int angle_pg; // Polygon angle
  318. wxPoint shape_offset = wxPoint( 0, dy - dx );
  319. RotatePoint( &shape_offset, angle ); // Rotating shape offset vector with component
  320. for( ii = 0; ii < aCircleToSegmentsCount / 2 + 1; ii++ ) // Half circle end cap...
  321. {
  322. corner_position = wxPoint( dx, 0 );
  323. // Coordinate translation +dx
  324. RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) );
  325. RotatePoint( &corner_position, angle );
  326. angle_pg = ii * delta;
  327. RotatePoint( &corner_position, angle_pg );
  328. corner_position += PadShapePos - shape_offset;
  329. CPolyPt polypoint( corner_position.x, corner_position.y );
  330. aCornerBuffer.push_back( polypoint );
  331. }
  332. for( ii = 0; ii < aCircleToSegmentsCount / 2 + 1; ii++ ) // Second half circle end cap...
  333. {
  334. corner_position = wxPoint( -dx, 0 );
  335. // Coordinate translation -dx
  336. RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) );
  337. RotatePoint( &corner_position, angle );
  338. angle_pg = ii * delta;
  339. RotatePoint( &corner_position, angle_pg );
  340. corner_position += PadShapePos + shape_offset;
  341. CPolyPt polypoint( corner_position.x, corner_position.y );
  342. aCornerBuffer.push_back( polypoint );
  343. }
  344. aCornerBuffer.back().end_contour = true;
  345. break;
  346. }
  347. else //if( dy <= dx )
  348. {
  349. dx = (int) ( dx * aCorrectionFactor );
  350. int angle_pg; // Polygon angle
  351. wxPoint shape_offset = wxPoint( (dy - dx), 0 );
  352. RotatePoint( &shape_offset, angle );
  353. for( ii = 0; ii < aCircleToSegmentsCount / 2 + 1; ii++ )
  354. {
  355. corner_position = wxPoint( 0, dy );
  356. RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) );
  357. RotatePoint( &corner_position, angle );
  358. angle_pg = ii * delta;
  359. RotatePoint( &corner_position, angle_pg );
  360. corner_position += PadShapePos - shape_offset;
  361. CPolyPt polypoint( corner_position.x, corner_position.y );
  362. aCornerBuffer.push_back( polypoint );
  363. }
  364. for( ii = 0; ii < aCircleToSegmentsCount / 2 + 1; ii++ )
  365. {
  366. corner_position = wxPoint( 0, -dy );
  367. RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) );
  368. RotatePoint( &corner_position, angle );
  369. angle_pg = ii * delta;
  370. RotatePoint( &corner_position, angle_pg );
  371. corner_position += PadShapePos + shape_offset;
  372. CPolyPt polypoint( corner_position.x, corner_position.y );
  373. aCornerBuffer.push_back( polypoint );
  374. }
  375. aCornerBuffer.back().end_contour = true;
  376. break;
  377. }
  378. default:
  379. case PAD_TRAPEZOID:
  380. psize.x += ABS( m_DeltaSize.y );
  381. psize.y += ABS( m_DeltaSize.x );
  382. // fall through
  383. case PAD_RECT:
  384. // Easy implementation for rectangular cutouts with rounded corners
  385. angle = m_Orient;
  386. // Corner rounding radius
  387. int rounding_radius = (int) ( aClearanceValue * aCorrectionFactor );
  388. int angle_pg; // Polygon increment angle
  389. for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ )
  390. {
  391. corner_position = wxPoint( 0, -rounding_radius );
  392. RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) );
  393. // Start at half increment offset
  394. angle_pg = i * delta;
  395. RotatePoint( &corner_position, angle_pg );
  396. // Rounding vector rotation
  397. corner_position -= psize / 2; // Rounding vector + Pad corner offset
  398. RotatePoint( &corner_position, angle );
  399. // Rotate according to module orientation
  400. corner_position += PadShapePos; // Shift origin to position
  401. CPolyPt polypoint( corner_position.x, corner_position.y );
  402. aCornerBuffer.push_back( polypoint );
  403. }
  404. for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ )
  405. {
  406. corner_position = wxPoint( -rounding_radius, 0 );
  407. RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) );
  408. angle_pg = i * delta;
  409. RotatePoint( &corner_position, angle_pg );
  410. corner_position -= wxPoint( psize.x / 2, -psize.y / 2 );
  411. RotatePoint( &corner_position, angle );
  412. corner_position += PadShapePos;
  413. CPolyPt polypoint( corner_position.x, corner_position.y );
  414. aCornerBuffer.push_back( polypoint );
  415. }
  416. for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ )
  417. {
  418. corner_position = wxPoint( 0, rounding_radius );
  419. RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) );
  420. angle_pg = i * delta;
  421. RotatePoint( &corner_position, angle_pg );
  422. corner_position += psize / 2;
  423. RotatePoint( &corner_position, angle );
  424. corner_position += PadShapePos;
  425. CPolyPt polypoint( corner_position.x, corner_position.y );
  426. aCornerBuffer.push_back( polypoint );
  427. }
  428. for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ )
  429. {
  430. corner_position = wxPoint( rounding_radius, 0 );
  431. RotatePoint( &corner_position, (1800 / aCircleToSegmentsCount) );
  432. angle_pg = i * delta;
  433. RotatePoint( &corner_position, angle_pg );
  434. corner_position -= wxPoint( -psize.x / 2, psize.y / 2 );
  435. RotatePoint( &corner_position, angle );
  436. corner_position += PadShapePos;
  437. CPolyPt polypoint( corner_position.x, corner_position.y );
  438. aCornerBuffer.push_back( polypoint );
  439. }
  440. aCornerBuffer.back().end_contour = true;
  441. break;
  442. }
  443. }
  444. /**
  445. * Function CreateThermalReliefPadPolygon
  446. * Add holes around a pad to create a thermal relief
  447. * copper thickness is min (dx/2, aCopperWitdh) or min (dy/2, aCopperWitdh)
  448. * @param aCornerBuffer = a buffer to store the polygon
  449. * @param aPad = the current pad used to create the thermal shape
  450. * @param aThermalGap = gap in thermal shape
  451. * @param aCopperThickness = stubs thickness in thermal shape
  452. * @param aMinThicknessValue = min copper thickness allowed
  453. * @param aCircleToSegmentsCount = the number of segments to approximate a circle
  454. * @param aCorrectionFactor = the correction to apply to circles radius to keep
  455. * @param aThermalRot = for rond pads the rotation of thermal stubs (450 usually for 45 deg.)
  456. */
  457. /* thermal reliefs are created as 4 polygons.
  458. * each corner of a polygon if calculated for a pad at position 0, 0, orient 0,
  459. * and then moved and rotated acroding to the pad position and orientation
  460. */
  461. /* WARNING:
  462. * When Kbool calculates the filled areas :
  463. * i.e when substracting holes (thermal shapes) to the full zone area
  464. * under certains circumstances kboll drop some holes.
  465. * These circumstances are:
  466. * some identical holes (same thermal shape and size) are *exactly* on the same vertical line
  467. * And
  468. * nothing else between holes
  469. * And
  470. * angles less than 90 deg between 2 consecutive lines in hole outline (sometime occurs without
  471. * this condition)
  472. * And
  473. * a hole above the identical holes
  474. *
  475. * In fact, it is easy to find these conditions in pad arrays.
  476. * So to avoid this, the workaround is do not use holes outlines that include
  477. * angles less than 90 deg between 2 consecutive lines
  478. * this is made in round and oblong thermal reliefs
  479. *
  480. * Note 1: polygons are drawm using outlines witk a thickness = aMinThicknessValue
  481. * so shapes must keep in account this outline thickness
  482. *
  483. * Note 2:
  484. * Trapezoidal pads are not considered here because they are very special case
  485. * and are used in microwave applications and they *DO NOT* have a thermal relief that
  486. * change the shape by creating stubs and destroy their properties.
  487. */
  488. void CreateThermalReliefPadPolygon( std::vector<CPolyPt>& aCornerBuffer,
  489. D_PAD& aPad,
  490. int aThermalGap,
  491. int aCopperThickness,
  492. int aMinThicknessValue,
  493. int aCircleToSegmentsCount,
  494. double aCorrectionFactor,
  495. int aThermalRot )
  496. {
  497. wxPoint corner, corner_end;
  498. wxPoint PadShapePos = aPad.ReturnShapePos(); /* Note: for pad having a shape offset,
  499. * the pad position is NOT the shape position */
  500. wxSize copper_thickness;
  501. int dx = aPad.m_Size.x / 2;
  502. int dy = aPad.m_Size.y / 2;
  503. int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree
  504. /* Keep in account the polygon outline thickness
  505. * aThermalGap must be increased by aMinThicknessValue/2 because drawing external outline
  506. * with a thickness of aMinThicknessValue will reduce gap by aMinThicknessValue/2
  507. */
  508. aThermalGap += aMinThicknessValue / 2;
  509. /* Keep in account the polygon outline thickness
  510. * copper_thickness must be decreased by aMinThicknessValue because drawing outlines
  511. * with a thickness of aMinThicknessValue will increase real thickness by aMinThicknessValue
  512. */
  513. aCopperThickness -= aMinThicknessValue;
  514. if( aCopperThickness < 0 )
  515. aCopperThickness = 0;
  516. copper_thickness.x = min( dx, aCopperThickness );
  517. copper_thickness.y = min( dy, aCopperThickness );
  518. switch( aPad.m_PadShape )
  519. {
  520. case PAD_CIRCLE: // Add 4 similar holes
  521. {
  522. /* we create 4 copper holes and put them in position 1, 2, 3 and 4
  523. * here is the area of the rectangular pad + its thermal gap
  524. * the 4 copper holes remove the copper in order to create the thermal gap
  525. * 4 ------ 1
  526. * | |
  527. * | |
  528. * | |
  529. * | |
  530. * 3 ------ 2
  531. * holes 2, 3, 4 are the same as hole 1, rotated 90, 180, 270 deg
  532. */
  533. // Build the hole pattern, for the hole in the X >0, Y > 0 plane:
  534. // The pattern roughtly is a 90 deg arc pie
  535. std::vector <wxPoint> corners_buffer;
  536. // Radius of outer arcs of the shape corrected for arc approximation by lines
  537. int outer_radius = (int) ( (dx + aThermalGap) * aCorrectionFactor );
  538. // Crosspoint of thermal spoke sides, the first point of polygon buffer
  539. corners_buffer.push_back( wxPoint( copper_thickness.x / 2, copper_thickness.y / 2 ) );
  540. // Add an intermediate point on spoke sides, to allow a > 90 deg angle between side
  541. // and first seg of arc approx
  542. corner.x = copper_thickness.x / 2;
  543. int y = outer_radius - (aThermalGap / 4);
  544. corner.y = (int) sqrt( ( ( (double) y * y ) - (double) corner.x * corner.x ) );
  545. if( aThermalRot != 0 )
  546. corners_buffer.push_back( corner );
  547. // calculate the starting point of the outter arc
  548. corner.x = copper_thickness.x / 2;
  549. double dtmp = sqrt( ( (double) outer_radius * outer_radius ) -
  550. ( (double) corner.x * corner.x ) );
  551. corner.y = (int) dtmp;
  552. RotatePoint( &corner, 90 );
  553. // calculate the ending point of the outter arc
  554. corner_end.x = corner.y;
  555. corner_end.y = corner.x;
  556. // calculate intermediate points (y coordinate from corner.y to corner_end.y
  557. while( (corner.y > corner_end.y) && (corner.x < corner_end.x) )
  558. {
  559. corners_buffer.push_back( corner );
  560. RotatePoint( &corner, delta );
  561. }
  562. corners_buffer.push_back( corner_end );
  563. /* add an intermediate point, to avoid angles < 90 deg between last arc approx line
  564. * and radius line
  565. */
  566. corner.x = corners_buffer[1].y;
  567. corner.y = corners_buffer[1].x;
  568. corners_buffer.push_back( corner );
  569. // Now, add the 4 holes ( each is the pattern, rotated by 0, 90, 180 and 270 deg
  570. // aThermalRot = 450 (45.0 degrees orientation) work fine.
  571. int angle_pad = aPad.GetOrientation(); // Pad orientation
  572. int th_angle = aThermalRot;
  573. for( unsigned ihole = 0; ihole < 4; ihole++ )
  574. {
  575. for( unsigned ii = 0; ii < corners_buffer.size(); ii++ )
  576. {
  577. corner = corners_buffer[ii];
  578. RotatePoint( &corner, th_angle + angle_pad ); // Rotate by segment angle and pad orientation
  579. corner += PadShapePos;
  580. aCornerBuffer.push_back( CPolyPt( corner.x, corner.y ) );
  581. }
  582. aCornerBuffer.back().end_contour = true;
  583. th_angle += 900; // Note: th_angle in in 0.1 deg.
  584. }
  585. }
  586. break;
  587. case PAD_OVAL:
  588. {
  589. // Oval pad support along the lines of round and rectangular pads
  590. std::vector <wxPoint> corners_buffer; // Polygon buffer as vector
  591. int dx = (aPad.m_Size.x / 2) + aThermalGap; // Cutout radius x
  592. int dy = (aPad.m_Size.y / 2) + aThermalGap; // Cutout radius y
  593. wxPoint shape_offset;
  594. // We want to calculate an oval shape with dx > dy.
  595. // if this is not the case, exchange dx and dy, and rotate the shape 90 deg.
  596. int supp_angle = 0;
  597. if( dx < dy )
  598. {
  599. EXCHG( dx, dy );
  600. supp_angle = 900;
  601. EXCHG( copper_thickness.x, copper_thickness.y );
  602. }
  603. int deltasize = dx - dy; // = distance between shape position and the 2 demi-circle ends centre
  604. // here we have dx > dy
  605. // Radius of outer arcs of the shape:
  606. int outer_radius = dy; // The radius of the outer arc is radius end + aThermalGap
  607. // Some coordinate fiddling, depending on the shape offset direction
  608. shape_offset = wxPoint( deltasize, 0 );
  609. // Crosspoint of thermal spoke sides, the first point of polygon buffer
  610. corners_buffer.push_back( wxPoint( copper_thickness.x / 2, copper_thickness.y / 2 ) );
  611. // Arc start point calculation, the intersecting point of cutout arc and thermal spoke edge
  612. if( copper_thickness.x > deltasize ) // If copper thickness is more than shape offset, we need to calculate arc intercept point.
  613. {
  614. corner.x = copper_thickness.x / 2;
  615. corner.y = (int) sqrt( ( (double) outer_radius * outer_radius ) -
  616. ( (double) ( corner.x - delta ) * ( corner.x - deltasize ) ) );
  617. corner.x -= deltasize;
  618. /* creates an intermediate point, to have a > 90 deg angle
  619. * between the side and the first segment of arc approximation
  620. */
  621. wxPoint intpoint = corner;
  622. intpoint.y -= aThermalGap / 4;
  623. corners_buffer.push_back( intpoint + shape_offset );
  624. RotatePoint( &corner, 90 );
  625. }
  626. else
  627. {
  628. corner.x = copper_thickness.x / 2;
  629. corner.y = outer_radius;
  630. corners_buffer.push_back( corner );
  631. corner.x = ( deltasize - copper_thickness.x ) / 2;
  632. }
  633. // Add an intermediate point on spoke sides, to allow a > 90 deg angle between side
  634. // and first seg of arc approx
  635. wxPoint last_corner;
  636. last_corner.y = copper_thickness.y / 2;
  637. int px = outer_radius - (aThermalGap / 4);
  638. last_corner.x =
  639. (int) sqrt( ( ( (double) px * px ) - (double) last_corner.y * last_corner.y ) );
  640. // Arc stop point calculation, the intersecting point of cutout arc and thermal spoke edge
  641. corner_end.y = copper_thickness.y / 2;
  642. corner_end.x =
  643. (int) sqrt( ( (double) outer_radius *
  644. outer_radius ) - ( (double) corner_end.y * corner_end.y ) );
  645. RotatePoint( &corner_end, -90 );
  646. // calculate intermediate arc points till limit is reached
  647. while( (corner.y > corner_end.y) && (corner.x < corner_end.x) )
  648. {
  649. corners_buffer.push_back( corner + shape_offset );
  650. RotatePoint( &corner, delta );
  651. }
  652. //corners_buffer.push_back(corner + shape_offset); // TODO: about one mil geometry error forms somewhere.
  653. corners_buffer.push_back( corner_end + shape_offset );
  654. corners_buffer.push_back( last_corner + shape_offset ); // Enabling the line above shows intersection point.
  655. /* Create 2 holes, rotated by pad rotation.
  656. */
  657. int angle = aPad.GetOrientation() + supp_angle;
  658. for( int irect = 0; irect < 2; irect++ )
  659. {
  660. for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
  661. {
  662. wxPoint cpos = corners_buffer[ic];
  663. RotatePoint( &cpos, angle );
  664. cpos += PadShapePos;
  665. aCornerBuffer.push_back( CPolyPt( cpos.x, cpos.y ) );
  666. }
  667. aCornerBuffer.back().end_contour = true;
  668. angle += 1800; // this is calculate hole 3
  669. if( angle >= 3600 )
  670. angle -= 3600;
  671. }
  672. // Create holes, that are the mirrored from the previous holes
  673. for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
  674. {
  675. wxPoint swap = corners_buffer[ic];
  676. swap.x = -swap.x;
  677. corners_buffer[ic] = swap;
  678. }
  679. // Now add corner 4 and 2 (2 is the corner 4 rotated by 180 deg
  680. angle = aPad.GetOrientation() + supp_angle;
  681. for( int irect = 0; irect < 2; irect++ )
  682. {
  683. for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
  684. {
  685. wxPoint cpos = corners_buffer[ic];
  686. RotatePoint( &cpos, angle );
  687. cpos += PadShapePos;
  688. aCornerBuffer.push_back( CPolyPt( cpos.x, cpos.y ) );
  689. }
  690. aCornerBuffer.back().end_contour = true;
  691. angle += 1800;
  692. if( angle >= 3600 )
  693. angle -= 3600;
  694. }
  695. }
  696. break;
  697. case PAD_RECT: // draw 4 Holes
  698. {
  699. /* we create 4 copper holes and put them in position 1, 2, 3 and 4
  700. * here is the area of the rectangular pad + its thermal gap
  701. * the 4 copper holes remove the copper in order to create the thermal gap
  702. * 4 ------ 1
  703. * | |
  704. * | |
  705. * | |
  706. * | |
  707. * 3 ------ 2
  708. * hole 3 is the same as hole 1, rotated 180 deg
  709. * hole 4 is the same as hole 2, rotated 180 deg and is the same as hole 1, mirrored
  710. */
  711. // First, create a rectangular hole for position 1 :
  712. // 2 ------- 3
  713. // | |
  714. // | |
  715. // | |
  716. // 1 -------4
  717. // Modified rectangles with one corner rounded. TODO: merging with oval thermals
  718. // and possibly round too.
  719. std::vector <wxPoint> corners_buffer; // Polygon buffer as vector
  720. int dx = (aPad.m_Size.x / 2) + aThermalGap; // Cutout radius x
  721. int dy = (aPad.m_Size.y / 2) + aThermalGap; // Cutout radius y
  722. // The first point of polygon buffer is left lower corner, second the crosspoint of
  723. // thermal spoke sides, the third is upper right corner and the rest are rounding
  724. // vertices going anticlockwise. Note the inveted Y-axis in CG.
  725. corners_buffer.push_back( wxPoint( -dx, -(aThermalGap / 4 + copper_thickness.y / 2) ) ); // Adds small miters to zone
  726. corners_buffer.push_back( wxPoint( -(dx - aThermalGap / 4), -copper_thickness.y / 2 ) ); // fill and spoke corner
  727. corners_buffer.push_back( wxPoint( -copper_thickness.x / 2, -copper_thickness.y / 2 ) );
  728. corners_buffer.push_back( wxPoint( -copper_thickness.x / 2, -(dy - aThermalGap / 4) ) );
  729. corners_buffer.push_back( wxPoint( -(aThermalGap / 4 + copper_thickness.x / 2), -dy ) );
  730. int angle = aPad.GetOrientation();
  731. int rounding_radius = (int) ( aThermalGap * aCorrectionFactor ); // Corner rounding radius
  732. int angle_pg; // Polygon increment angle
  733. for( int i = 0; i < aCircleToSegmentsCount / 4 + 1; i++ )
  734. {
  735. wxPoint corner_position = wxPoint( 0, -rounding_radius );
  736. RotatePoint( &corner_position, 1800 / aCircleToSegmentsCount ); // Start at half increment offset
  737. angle_pg = i * delta;
  738. RotatePoint( &corner_position, angle_pg ); // Rounding vector rotation
  739. corner_position -= aPad.m_Size / 2; // Rounding vector + Pad corner offset
  740. corners_buffer.push_back( wxPoint( corner_position.x, corner_position.y ) );
  741. }
  742. for( int irect = 0; irect < 2; irect++ )
  743. {
  744. for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
  745. {
  746. wxPoint cpos = corners_buffer[ic];
  747. RotatePoint( &cpos, angle ); // Rotate according to module orientation
  748. cpos += PadShapePos; // Shift origin to position
  749. aCornerBuffer.push_back( CPolyPt( cpos.x, cpos.y ) );
  750. }
  751. aCornerBuffer.back().end_contour = true;
  752. angle += 1800; // this is calculate hole 3
  753. if( angle >= 3600 )
  754. angle -= 3600;
  755. }
  756. // Create holes, that are the mirrored from the previous holes
  757. for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
  758. {
  759. wxPoint swap = corners_buffer[ic];
  760. swap.x = -swap.x;
  761. corners_buffer[ic] = swap;
  762. }
  763. // Now add corner 4 and 2 (2 is the corner 4 rotated by 180 deg
  764. for( int irect = 0; irect < 2; irect++ )
  765. {
  766. for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
  767. {
  768. wxPoint cpos = corners_buffer[ic];
  769. RotatePoint( &cpos, angle );
  770. cpos += PadShapePos;
  771. aCornerBuffer.push_back( CPolyPt( cpos.x, cpos.y ) );
  772. }
  773. aCornerBuffer.back().end_contour = true;
  774. angle += 1800;
  775. if( angle >= 3600 )
  776. angle -= 3600;
  777. }
  778. break;
  779. }
  780. }
  781. }