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.

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