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.

526 lines
15 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. /**
  25. * @file class_pad_custom_shape_functions.cpp
  26. * D_PAD functions specific to custom shaped pads.
  27. */
  28. #include <fctsys.h>
  29. #include <trigo.h>
  30. #include <pcbnew.h>
  31. #include <bezier_curves.h>
  32. #include <class_board.h>
  33. #include <class_board_item.h>
  34. #include <class_drawsegment.h>
  35. #include <class_edge_mod.h>
  36. #include <class_pad.h>
  37. #include <convert_basic_shapes_to_polygon.h>
  38. #include <geometry/convex_hull.h>
  39. #include <geometry/geometry_utils.h>
  40. #include <geometry/shape_rect.h>
  41. void PAD_CS_PRIMITIVE::ExportTo( DRAWSEGMENT* aTarget )
  42. {
  43. aTarget->SetShape( m_Shape );
  44. aTarget->SetWidth( m_Thickness );
  45. aTarget->SetStart( m_Start );
  46. aTarget->SetEnd( m_End );
  47. aTarget->SetBezControl1( m_Ctrl1 );
  48. aTarget->SetBezControl2( m_Ctrl2 );
  49. // in a DRAWSEGMENT the radius of a circle is calculated from the
  50. // center and one point on the circle outline (stored in m_End)
  51. if( m_Shape == S_CIRCLE )
  52. {
  53. wxPoint end = m_Start;
  54. end.x += m_Radius;
  55. aTarget->SetEnd( end );
  56. }
  57. aTarget->SetAngle( m_ArcAngle );
  58. aTarget->SetPolyPoints( m_Poly );
  59. }
  60. void PAD_CS_PRIMITIVE::ExportTo( EDGE_MODULE* aTarget )
  61. {
  62. ExportTo( static_cast<DRAWSEGMENT*>( aTarget ) );
  63. // Initialize coordinates specific to the EDGE_MODULE (m_Start0 and m_End0)
  64. aTarget->SetLocalCoord();
  65. }
  66. void PAD_CS_PRIMITIVE::Move( wxPoint aMoveVector )
  67. {
  68. m_Start += aMoveVector;
  69. m_End += aMoveVector;
  70. m_Ctrl1 += aMoveVector;
  71. m_Ctrl2 += aMoveVector;
  72. for( auto& corner : m_Poly )
  73. corner += aMoveVector;
  74. }
  75. void PAD_CS_PRIMITIVE::Rotate( const wxPoint& aRotCentre, double aAngle )
  76. {
  77. switch( m_Shape )
  78. {
  79. case S_ARC:
  80. case S_SEGMENT:
  81. case S_CIRCLE:
  82. // these can all be done by just rotating the start and end points
  83. RotatePoint( &m_Start, aRotCentre, aAngle );
  84. RotatePoint( &m_End, aRotCentre, aAngle );
  85. break;
  86. case S_RECT:
  87. if( KiROUND( aAngle ) % 900 == 0 )
  88. {
  89. RotatePoint( &m_Start, aRotCentre, aAngle );
  90. RotatePoint( &m_End, aRotCentre, aAngle );
  91. break;
  92. }
  93. // Convert non-cartesian-rotated rect to a diamond
  94. m_Shape = S_POLYGON;
  95. m_Poly.clear();
  96. m_Poly.emplace_back( m_Start );
  97. m_Poly.emplace_back( m_End.x, m_Start.y );
  98. m_Poly.emplace_back( m_End );
  99. m_Poly.emplace_back( m_Start.x, m_End.y );
  100. KI_FALLTHROUGH;
  101. case S_POLYGON:
  102. for( auto& pt : m_Poly )
  103. RotatePoint( &pt, aRotCentre, aAngle );
  104. break;
  105. case S_CURVE:
  106. RotatePoint( &m_Start, aRotCentre, aAngle );
  107. RotatePoint( &m_End, aRotCentre, aAngle );
  108. RotatePoint( &m_Ctrl1, aRotCentre, aAngle );
  109. RotatePoint( &m_Ctrl2, aRotCentre, aAngle );
  110. break;
  111. default:
  112. // un-handled edge transform
  113. wxASSERT_MSG( false, wxT( "PAD_CS_PRIMITIVE::Rotate not implemented for "
  114. + BOARD_ITEM::ShowShape( m_Shape ) ) );
  115. break;
  116. }
  117. }
  118. /*
  119. * Has meaning only for free shape pads.
  120. * add a free shape to the shape list.
  121. * the shape is a polygon (can be with thick outline), segment, circle or arc
  122. */
  123. void D_PAD::AddPrimitivePoly( const SHAPE_POLY_SET& aPoly, int aThickness, bool aMergePrimitives )
  124. {
  125. std::vector<wxPoint> points;
  126. // If aPoly has holes, convert it to a polygon with no holes.
  127. SHAPE_POLY_SET poly_no_hole;
  128. poly_no_hole.Append( aPoly );
  129. poly_no_hole.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  130. for( auto iter = poly_no_hole.CIterate(); iter; iter++ )
  131. points.emplace_back( iter->x, iter->y );
  132. AddPrimitivePoly( points, aThickness, aMergePrimitives );
  133. }
  134. void D_PAD::AddPrimitivePoly( const std::vector<wxPoint>& aPoly, int aThickness,
  135. bool aMergePrimitives )
  136. {
  137. PAD_CS_PRIMITIVE shape( S_POLYGON );
  138. shape.m_Poly = aPoly;
  139. shape.m_Thickness = aThickness;
  140. m_basicShapes.push_back( shape );
  141. if( aMergePrimitives )
  142. MergePrimitivesAsPolygon();
  143. }
  144. void D_PAD::AddPrimitiveSegment( const wxPoint& aStart, const wxPoint& aEnd, int aThickness,
  145. bool aMergePrimitives )
  146. {
  147. PAD_CS_PRIMITIVE shape( S_SEGMENT );
  148. shape.m_Start = aStart;
  149. shape.m_End = aEnd;
  150. shape.m_Thickness = aThickness;
  151. m_basicShapes.push_back( shape );
  152. if( aMergePrimitives )
  153. MergePrimitivesAsPolygon();
  154. }
  155. void D_PAD::AddPrimitiveArc( const wxPoint& aCenter, const wxPoint& aStart, int aArcAngle,
  156. int aThickness, bool aMergePrimitives )
  157. {
  158. PAD_CS_PRIMITIVE shape( S_ARC );
  159. shape.m_Start = aCenter;
  160. shape.m_End = aStart;
  161. shape.m_ArcAngle = aArcAngle;
  162. shape.m_Thickness = aThickness;
  163. m_basicShapes.push_back( shape );
  164. if( aMergePrimitives )
  165. MergePrimitivesAsPolygon();
  166. }
  167. void D_PAD::AddPrimitiveCurve( const wxPoint& aStart, const wxPoint& aEnd, const wxPoint& aCtrl1,
  168. const wxPoint& aCtrl2, int aThickness, bool aMergePrimitives )
  169. {
  170. PAD_CS_PRIMITIVE shape( S_CURVE );
  171. shape.m_Start = aStart;
  172. shape.m_End = aEnd;
  173. shape.m_Ctrl1 = aCtrl1;
  174. shape.m_Ctrl2 = aCtrl2;
  175. shape.m_Thickness = aThickness;
  176. m_basicShapes.push_back( shape );
  177. if( aMergePrimitives )
  178. MergePrimitivesAsPolygon();
  179. }
  180. void D_PAD::AddPrimitiveCircle( const wxPoint& aCenter, int aRadius, int aThickness,
  181. bool aMergePrimitives )
  182. {
  183. PAD_CS_PRIMITIVE shape( S_CIRCLE );
  184. shape.m_Start = aCenter;
  185. shape.m_Radius = aRadius;
  186. shape.m_Thickness = aThickness;
  187. m_basicShapes.push_back( shape );
  188. if( aMergePrimitives )
  189. MergePrimitivesAsPolygon();
  190. }
  191. void D_PAD::AddPrimitiveRect( const wxPoint& aStart, const wxPoint& aEnd, int aThickness,
  192. bool aMergePrimitives )
  193. {
  194. PAD_CS_PRIMITIVE shape( S_RECT );
  195. shape.m_Start = aStart;
  196. shape.m_End = aEnd;
  197. shape.m_Thickness = aThickness;
  198. m_basicShapes.push_back( shape );
  199. if( aMergePrimitives )
  200. MergePrimitivesAsPolygon();
  201. }
  202. bool D_PAD::SetPrimitives( const std::vector<PAD_CS_PRIMITIVE>& aPrimitivesList )
  203. {
  204. // clear old list
  205. m_basicShapes.clear();
  206. // Import to the basic shape list
  207. if( aPrimitivesList.size() )
  208. m_basicShapes = aPrimitivesList;
  209. // Only one polygon is expected (pad area = only one copper area)
  210. return MergePrimitivesAsPolygon();
  211. }
  212. bool D_PAD::AddPrimitives( const std::vector<PAD_CS_PRIMITIVE>& aPrimitivesList )
  213. {
  214. for( const auto& prim : aPrimitivesList )
  215. m_basicShapes.push_back( prim );
  216. return MergePrimitivesAsPolygon();
  217. }
  218. // clear the basic shapes list and associated data
  219. void D_PAD::DeletePrimitivesList()
  220. {
  221. m_basicShapes.clear();
  222. m_customShapeAsPolygon.RemoveAllContours();
  223. }
  224. bool D_PAD::buildCustomPadPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError )
  225. {
  226. SHAPE_POLY_SET aux_polyset;
  227. for( PAD_CS_PRIMITIVE& bshape : m_basicShapes )
  228. {
  229. switch( bshape.m_Shape )
  230. {
  231. case S_CURVE:
  232. {
  233. std::vector<wxPoint> ctrlPoints = { bshape.m_Start, bshape.m_Ctrl1, bshape.m_Ctrl2, bshape.m_End };
  234. BEZIER_POLY converter( ctrlPoints );
  235. std::vector< wxPoint> poly;
  236. converter.GetPoly( poly, bshape.m_Thickness );
  237. for( unsigned ii = 1; ii < poly.size(); ii++ )
  238. {
  239. TransformSegmentToPolygon(
  240. aux_polyset, poly[ ii - 1 ], poly[ ii ], aError, bshape.m_Thickness );
  241. }
  242. break;
  243. }
  244. case S_SEGMENT: // usual segment : line with rounded ends
  245. {
  246. TransformSegmentToPolygon( aux_polyset, bshape.m_Start, bshape.m_End, aError,
  247. bshape.m_Thickness );
  248. break;
  249. }
  250. case S_ARC: // Arc with rounded ends
  251. {
  252. TransformArcToPolygon( aux_polyset, bshape.m_Start, bshape.m_End, bshape.m_ArcAngle,
  253. aError, bshape.m_Thickness );
  254. break;
  255. }
  256. case S_CIRCLE: // ring or circle
  257. {
  258. if( bshape.m_Thickness ) // ring
  259. TransformRingToPolygon( aux_polyset, bshape.m_Start, bshape.m_Radius, aError,
  260. bshape.m_Thickness );
  261. else // Filled circle
  262. TransformCircleToPolygon( aux_polyset, bshape.m_Start, bshape.m_Radius, aError );
  263. break;
  264. }
  265. case S_RECT:
  266. bshape.m_Poly.clear();
  267. bshape.m_Poly.emplace_back( bshape.m_Start );
  268. bshape.m_Poly.emplace_back( bshape.m_End.x, bshape.m_Start.y );
  269. bshape.m_Poly.emplace_back( bshape.m_End );
  270. bshape.m_Poly.emplace_back( bshape.m_Start.x, bshape.m_End.y );
  271. KI_FALLTHROUGH;
  272. case S_POLYGON: // polygon
  273. {
  274. if( bshape.m_Poly.size() < 2 )
  275. break; // Malformed polygon.
  276. // Insert the polygon:
  277. const std::vector< wxPoint>& poly = bshape.m_Poly;
  278. aux_polyset.NewOutline();
  279. if( bshape.m_Thickness )
  280. {
  281. SHAPE_POLY_SET polyset;
  282. polyset.NewOutline();
  283. for( const wxPoint& pt : poly )
  284. polyset.Append( pt.x, pt.y );
  285. int numSegs = std::max( GetArcToSegmentCount( bshape.m_Thickness / 2, aError, 360.0 ), 6 );
  286. polyset.Inflate( bshape.m_Thickness / 2, numSegs );
  287. aux_polyset.Append( polyset );
  288. }
  289. else
  290. {
  291. for( const wxPoint& pt : poly )
  292. aux_polyset.Append( pt.x, pt.y );
  293. }
  294. if( bshape.m_Shape == S_RECT )
  295. bshape.m_Poly.clear();
  296. }
  297. break;
  298. default:
  299. // un-handled primitive
  300. wxASSERT_MSG( false, wxT( "D_PAD::buildCustomPadPolygon not implemented for "
  301. + BOARD_ITEM::ShowShape( bshape.m_Shape ) ) );
  302. break;
  303. }
  304. }
  305. aux_polyset.Simplify( SHAPE_POLY_SET::PM_FAST );
  306. // Merge all polygons with the initial pad anchor shape
  307. if( aux_polyset.OutlineCount() )
  308. {
  309. aMergedPolygon->BooleanAdd( aux_polyset, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  310. aMergedPolygon->Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  311. }
  312. return aMergedPolygon->OutlineCount() <= 1;
  313. }
  314. /* Merge all basic shapes, converted to a polygon in one polygon,
  315. * return true if OK, false in there is more than one polygon
  316. * in aMergedPolygon
  317. */
  318. bool D_PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon )
  319. {
  320. auto board = GetBoard();
  321. int maxError = ARC_HIGH_DEF;
  322. if( board )
  323. maxError = board->GetDesignSettings().m_MaxError;
  324. // if aMergedPolygon == NULL, use m_customShapeAsPolygon as target
  325. if( !aMergedPolygon )
  326. aMergedPolygon = &m_customShapeAsPolygon;
  327. aMergedPolygon->RemoveAllContours();
  328. // Add the anchor pad shape in aMergedPolygon, others in aux_polyset:
  329. // The anchor pad is always at 0,0
  330. switch( GetAnchorPadShape() )
  331. {
  332. default:
  333. case PAD_SHAPE_CIRCLE:
  334. TransformCircleToPolygon( *aMergedPolygon, wxPoint( 0, 0 ), GetSize().x / 2, maxError );
  335. break;
  336. case PAD_SHAPE_RECT:
  337. {
  338. SHAPE_RECT rect( -GetSize().x / 2, -GetSize().y / 2, GetSize().x, GetSize().y );
  339. aMergedPolygon->AddOutline( rect.Outline() );
  340. break;
  341. }
  342. }
  343. if( !buildCustomPadPolygon( aMergedPolygon, maxError ) )
  344. return false;
  345. m_boundingRadius = -1; // The current bounding radius is no longer valid.
  346. return aMergedPolygon->OutlineCount() <= 1;
  347. }
  348. void D_PAD::CustomShapeAsPolygonToBoardPosition( SHAPE_POLY_SET * aMergedPolygon,
  349. wxPoint aPosition, double aRotation ) const
  350. {
  351. if( aMergedPolygon->OutlineCount() == 0 )
  352. return;
  353. // Move, rotate, ... coordinates in aMergedPolygon according to the
  354. // pad position and orientation
  355. aMergedPolygon->Rotate( -DECIDEG2RAD( aRotation ) );
  356. aMergedPolygon->Move( VECTOR2I( aPosition ) );
  357. }
  358. bool D_PAD::GetBestAnchorPosition( VECTOR2I& aPos )
  359. {
  360. SHAPE_POLY_SET poly;
  361. if( !buildCustomPadPolygon( &poly, ARC_LOW_DEF ) )
  362. return false;
  363. const int minSteps = 10;
  364. const int maxSteps = 50;
  365. int stepsX, stepsY;
  366. auto bbox = poly.BBox();
  367. if( bbox.GetWidth() < bbox.GetHeight() )
  368. {
  369. stepsX = minSteps;
  370. stepsY = minSteps * (double) bbox.GetHeight() / (double )(bbox.GetWidth() + 1);
  371. }
  372. else
  373. {
  374. stepsY = minSteps;
  375. stepsX = minSteps * (double) bbox.GetWidth() / (double )(bbox.GetHeight() + 1);
  376. }
  377. stepsX = std::max(minSteps, std::min( maxSteps, stepsX ) );
  378. stepsY = std::max(minSteps, std::min( maxSteps, stepsY ) );
  379. VECTOR2I center = bbox.Centre();
  380. int64_t minDist = std::numeric_limits<int64_t>::max();
  381. int64_t minDistEdge;
  382. if( GetAnchorPadShape() == PAD_SHAPE_CIRCLE )
  383. {
  384. minDistEdge = GetSize().x;
  385. }
  386. else
  387. {
  388. minDistEdge = std::max( GetSize().x, GetSize().y );
  389. }
  390. OPT<VECTOR2I> bestAnchor( []()->OPT<VECTOR2I> { return NULLOPT; }() );
  391. for( int y = 0; y < stepsY ; y++ )
  392. {
  393. for( int x = 0; x < stepsX; x++ )
  394. {
  395. VECTOR2I p = bbox.GetPosition();
  396. p.x += rescale( x, bbox.GetWidth(), (stepsX - 1) );
  397. p.y += rescale( y, bbox.GetHeight(), (stepsY - 1) );
  398. if( poly.Contains(p) )
  399. {
  400. int dist = (center - p).EuclideanNorm();
  401. int distEdge = poly.COutline(0).Distance( p, true );
  402. if( distEdge >= minDistEdge )
  403. {
  404. if( dist < minDist )
  405. {
  406. bestAnchor = p;
  407. minDist = dist;
  408. }
  409. }
  410. }
  411. }
  412. }
  413. if( bestAnchor )
  414. {
  415. aPos = *bestAnchor;
  416. return true;
  417. }
  418. return false;
  419. }