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.

400 lines
11 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2016 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 1992-2016 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. * class D_PAD functions specific to custom shaped pads.
  27. */
  28. #include <fctsys.h>
  29. #include <PolyLine.h>
  30. #include <trigo.h>
  31. #include <pcbnew.h>
  32. #include <class_pad.h>
  33. #include <class_drawsegment.h>
  34. #include <class_edge_mod.h>
  35. #include <convert_basic_shapes_to_polygon.h>
  36. #include <geometry/shape_rect.h>
  37. #include <geometry/convex_hull.h>
  38. void PAD_CS_PRIMITIVE::ExportTo( DRAWSEGMENT* aTarget )
  39. {
  40. aTarget->SetShape( m_Shape );
  41. aTarget->SetWidth( m_Thickness );
  42. aTarget->SetStart( m_Start );
  43. aTarget->SetEnd( m_End );
  44. // in a DRAWSEGMENT the radius of a circle is calculated from the
  45. // center and one point on the circle outline (stored in m_End)
  46. if( m_Shape == S_CIRCLE )
  47. {
  48. wxPoint end = m_Start;
  49. end.x += m_Radius;
  50. aTarget->SetEnd( end );
  51. }
  52. aTarget->SetAngle( m_ArcAngle );
  53. aTarget->SetPolyPoints( m_Poly );
  54. }
  55. void PAD_CS_PRIMITIVE::ExportTo( EDGE_MODULE* aTarget )
  56. {
  57. ExportTo( static_cast<DRAWSEGMENT*>( aTarget ) );
  58. // Initialize coordinates specific to the EDGE_MODULE (m_Start0 and m_End0)
  59. aTarget->SetLocalCoord();
  60. }
  61. void PAD_CS_PRIMITIVE::Move( wxPoint aMoveVector )
  62. {
  63. m_Start += aMoveVector;
  64. m_End += aMoveVector;
  65. for( auto& corner : m_Poly )
  66. {
  67. corner += aMoveVector;
  68. }
  69. }
  70. /*
  71. * Has meaning only for free shape pads.
  72. * add a free shape to the shape list.
  73. * the shape is a polygon (can be with thick outline), segment, circle or arc
  74. */
  75. void D_PAD::AddPrimitive( const SHAPE_POLY_SET& aPoly, int aThickness )
  76. {
  77. std::vector<wxPoint> points;
  78. for( auto iter = aPoly.CIterate(); iter; iter++ )
  79. points.push_back( wxPoint( iter->x, iter->y ) );
  80. AddPrimitive( points, aThickness );
  81. }
  82. void D_PAD::AddPrimitive( const std::vector<wxPoint>& aPoly, int aThickness )
  83. {
  84. PAD_CS_PRIMITIVE shape( S_POLYGON );
  85. shape.m_Poly = aPoly;
  86. shape.m_Thickness = aThickness;
  87. m_basicShapes.push_back( shape );
  88. MergePrimitivesAsPolygon();
  89. }
  90. void D_PAD::AddPrimitive( wxPoint aStart, wxPoint aEnd, int aThickness )
  91. {
  92. PAD_CS_PRIMITIVE shape( S_SEGMENT );
  93. shape.m_Start = aStart;
  94. shape.m_End = aEnd;
  95. shape.m_Thickness = aThickness;
  96. m_basicShapes.push_back( shape );
  97. MergePrimitivesAsPolygon();
  98. }
  99. void D_PAD::AddPrimitive( wxPoint aCenter, wxPoint aStart, int aArcAngle, int aThickness )
  100. {
  101. PAD_CS_PRIMITIVE shape( S_ARC );
  102. shape.m_Start = aCenter;
  103. shape.m_End = aStart;
  104. shape.m_ArcAngle = aArcAngle;
  105. shape.m_Thickness = aThickness;
  106. m_basicShapes.push_back( shape );
  107. MergePrimitivesAsPolygon();
  108. }
  109. void D_PAD::AddPrimitive( wxPoint aCenter, int aRadius, int aThickness )
  110. {
  111. PAD_CS_PRIMITIVE shape( S_CIRCLE );
  112. shape.m_Start = aCenter;
  113. shape.m_Radius = aRadius;
  114. shape.m_Thickness = aThickness;
  115. m_basicShapes.push_back( shape );
  116. MergePrimitivesAsPolygon();
  117. }
  118. bool D_PAD::SetPrimitives( const std::vector<PAD_CS_PRIMITIVE>& aPrimitivesList )
  119. {
  120. // clear old list
  121. m_basicShapes.clear();
  122. // Import to the basic shape list
  123. if( aPrimitivesList.size() )
  124. m_basicShapes = aPrimitivesList;
  125. // Only one polygon is expected (pad area = only one copper area)
  126. return MergePrimitivesAsPolygon();
  127. }
  128. bool D_PAD::AddPrimitives( const std::vector<PAD_CS_PRIMITIVE>& aPrimitivesList )
  129. {
  130. for( const auto& prim : aPrimitivesList )
  131. m_basicShapes.push_back( prim );
  132. return MergePrimitivesAsPolygon();
  133. }
  134. // clear the basic shapes list and associated data
  135. void D_PAD::DeletePrimitivesList()
  136. {
  137. m_basicShapes.clear();
  138. m_customShapeAsPolygon.RemoveAllContours();
  139. }
  140. bool D_PAD::buildCustomPadPolygon( SHAPE_POLY_SET* aMergedPolygon,
  141. int aCircleToSegmentsCount )
  142. {
  143. SHAPE_POLY_SET aux_polyset;
  144. for( unsigned cnt = 0; cnt < m_basicShapes.size(); ++cnt )
  145. {
  146. const PAD_CS_PRIMITIVE& bshape = m_basicShapes[cnt];
  147. switch( bshape.m_Shape )
  148. {
  149. case S_SEGMENT: // usual segment : line with rounded ends
  150. TransformRoundedEndsSegmentToPolygon( aux_polyset,
  151. bshape.m_Start, bshape.m_End, aCircleToSegmentsCount, bshape.m_Thickness );
  152. break;
  153. case S_ARC: // Arc with rounded ends
  154. TransformArcToPolygon( aux_polyset,
  155. bshape.m_Start, bshape.m_End, bshape.m_ArcAngle,
  156. aCircleToSegmentsCount, bshape.m_Thickness );
  157. break;
  158. case S_CIRCLE: // ring or circle
  159. if( bshape.m_Thickness ) // ring
  160. TransformRingToPolygon( aux_polyset,
  161. bshape.m_Start, bshape.m_Radius,
  162. aCircleToSegmentsCount, bshape.m_Thickness ) ;
  163. else // Filled circle
  164. TransformCircleToPolygon( aux_polyset,
  165. bshape.m_Start, bshape.m_Radius,
  166. aCircleToSegmentsCount ) ;
  167. break;
  168. case S_POLYGON: // polygon
  169. if( bshape.m_Poly.size() < 2 )
  170. break; // Malformed polygon.
  171. {
  172. // Insert the polygon:
  173. const std::vector< wxPoint>& poly = bshape.m_Poly;
  174. aux_polyset.NewOutline();
  175. if( bshape.m_Thickness )
  176. {
  177. SHAPE_POLY_SET polyset;
  178. polyset.NewOutline();
  179. for( unsigned ii = 0; ii < poly.size(); ii++ )
  180. {
  181. polyset.Append( poly[ii].x, poly[ii].y );
  182. }
  183. polyset.Inflate( bshape.m_Thickness/2, 32 );
  184. aux_polyset.Append( polyset );
  185. }
  186. else
  187. for( unsigned ii = 0; ii < poly.size(); ii++ )
  188. aux_polyset.Append( poly[ii].x, poly[ii].y );
  189. }
  190. break;
  191. default:
  192. break;
  193. }
  194. }
  195. aux_polyset.Simplify( SHAPE_POLY_SET::PM_FAST );
  196. // Merge all polygons with the initial pad anchor shape
  197. if( aux_polyset.OutlineCount() )
  198. {
  199. aMergedPolygon->BooleanAdd( aux_polyset, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  200. aMergedPolygon->Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  201. }
  202. return aMergedPolygon->OutlineCount() <= 1;
  203. }
  204. /* Merge all basic shapes, converted to a polygon in one polygon,
  205. * return true if OK, false in there is more than one polygon
  206. * in aMergedPolygon
  207. */
  208. bool D_PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon,
  209. int aCircleToSegmentsCount )
  210. {
  211. // if aMergedPolygon == NULL, use m_customShapeAsPolygon as target
  212. if( !aMergedPolygon )
  213. aMergedPolygon = &m_customShapeAsPolygon;
  214. aMergedPolygon->RemoveAllContours();
  215. // Add the anchor pad shape in aMergedPolygon, others in aux_polyset:
  216. // The anchor pad is always at 0,0
  217. switch( GetAnchorPadShape() )
  218. {
  219. default:
  220. case PAD_SHAPE_CIRCLE:
  221. TransformCircleToPolygon( *aMergedPolygon, wxPoint( 0,0 ), GetSize().x/2,
  222. aCircleToSegmentsCount );
  223. break;
  224. case PAD_SHAPE_RECT:
  225. {
  226. SHAPE_RECT rect( -GetSize().x/2, -GetSize().y/2, GetSize().x, GetSize().y );
  227. aMergedPolygon->AddOutline( rect.Outline() );
  228. }
  229. break;
  230. }
  231. if ( !buildCustomPadPolygon( aMergedPolygon, aCircleToSegmentsCount ) )
  232. return false;
  233. m_boundingRadius = -1; // The current bouding radius is no more valid.
  234. return aMergedPolygon->OutlineCount() <= 1;
  235. }
  236. void D_PAD::CustomShapeAsPolygonToBoardPosition( SHAPE_POLY_SET * aMergedPolygon,
  237. wxPoint aPosition, double aRotation ) const
  238. {
  239. if( aMergedPolygon->OutlineCount() == 0 )
  240. return;
  241. // Move, rotate, ... coordinates in aMergedPolygon according to the
  242. // pad position and orientation
  243. for( int cnt = 0; cnt < aMergedPolygon->OutlineCount(); ++cnt )
  244. {
  245. SHAPE_LINE_CHAIN& poly = aMergedPolygon->Outline( cnt );
  246. for( int ii = 0; ii < poly.PointCount(); ++ii )
  247. {
  248. wxPoint corner( poly.Point( ii ).x, poly.Point( ii ).y );
  249. RotatePoint( &corner, aRotation );
  250. corner += aPosition;
  251. poly.Point( ii ).x = corner.x;
  252. poly.Point( ii ).y = corner.y;
  253. }
  254. }
  255. }
  256. bool D_PAD::GetBestAnchorPosition( VECTOR2I& aPos )
  257. {
  258. SHAPE_POLY_SET poly;
  259. if ( !buildCustomPadPolygon( &poly, 16 ) )
  260. return false;
  261. const int minSteps = 10;
  262. const int maxSteps = 50;
  263. int stepsX, stepsY;
  264. auto bbox = poly.BBox();
  265. if( bbox.GetWidth() < bbox.GetHeight() )
  266. {
  267. stepsX = minSteps;
  268. stepsY = minSteps * (double) bbox.GetHeight() / (double )(bbox.GetWidth() + 1);
  269. }
  270. else
  271. {
  272. stepsY = minSteps;
  273. stepsX = minSteps * (double) bbox.GetWidth() / (double )(bbox.GetHeight() + 1);
  274. }
  275. stepsX = std::max(minSteps, std::min( maxSteps, stepsX ) );
  276. stepsY = std::max(minSteps, std::min( maxSteps, stepsY ) );
  277. auto center = bbox.Centre();
  278. auto minDist = std::numeric_limits<int64_t>::max();
  279. int64_t minDistEdge;
  280. if( GetAnchorPadShape() == PAD_SHAPE_CIRCLE )
  281. {
  282. minDistEdge = GetSize().x;
  283. }
  284. else
  285. {
  286. minDistEdge = std::max( GetSize().x, GetSize().y );
  287. }
  288. OPT<VECTOR2I> bestAnchor( []()->OPT<VECTOR2I> { return NULLOPT; }() );
  289. for ( int y = 0; y < stepsY ; y++ )
  290. {
  291. for ( int x = 0; x < stepsX; x++ )
  292. {
  293. VECTOR2I p = bbox.GetPosition();
  294. p.x += rescale( x, bbox.GetWidth(), (stepsX - 1) );
  295. p.y += rescale( y, bbox.GetHeight(), (stepsY - 1) );
  296. if ( poly.Contains(p) )
  297. {
  298. auto dist = (center - p).EuclideanNorm();
  299. auto distEdge = poly.COutline(0).Distance( p, true );
  300. if ( distEdge >= minDistEdge )
  301. {
  302. if ( dist < minDist )
  303. {
  304. bestAnchor = p;
  305. minDist = dist;
  306. }
  307. }
  308. }
  309. }
  310. }
  311. if ( bestAnchor )
  312. {
  313. aPos = *bestAnchor;
  314. return true;
  315. }
  316. return false;
  317. }