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.

319 lines
9.2 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  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. #include <board.h>
  25. #include <board_design_settings.h>
  26. #include <board_item.h>
  27. #include <pcb_shape.h>
  28. #include <pad.h>
  29. #include <convert_basic_shapes_to_polygon.h>
  30. #include <geometry/shape_rect.h>
  31. /*
  32. * Has meaning only for free shape pads.
  33. * add a free shape to the shape list.
  34. * the shape is a polygon (can be with thick outline), segment, circle or arc
  35. */
  36. void PAD::AddPrimitivePoly( const SHAPE_POLY_SET& aPoly, int aThickness, bool aFilled )
  37. {
  38. // If aPoly has holes, convert it to a polygon with no holes.
  39. SHAPE_POLY_SET poly_no_hole;
  40. poly_no_hole.Append( aPoly );
  41. if( poly_no_hole.HasHoles() )
  42. poly_no_hole.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  43. // There should never be multiple shapes, but if there are, we split them into
  44. // primitives so that we can edit them both.
  45. for( int ii = 0; ii < poly_no_hole.OutlineCount(); ++ii )
  46. {
  47. SHAPE_POLY_SET poly_outline( poly_no_hole.COutline( ii ) );
  48. PCB_SHAPE* item = new PCB_SHAPE();
  49. item->SetShape( SHAPE_T::POLY );
  50. item->SetFilled( aFilled );
  51. item->SetPolyShape( poly_outline );
  52. item->SetWidth( aThickness );
  53. item->SetParent( this );
  54. m_editPrimitives.emplace_back( item );
  55. }
  56. SetDirty();
  57. }
  58. void PAD::AddPrimitivePoly( const std::vector<wxPoint>& aPoly, int aThickness, bool aFilled )
  59. {
  60. PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::POLY );
  61. item->SetFilled( aFilled );
  62. item->SetPolyPoints( aPoly );
  63. item->SetWidth( aThickness );
  64. item->SetParent( this );
  65. m_editPrimitives.emplace_back( item );
  66. SetDirty();
  67. }
  68. void PAD::AddPrimitiveSegment( const wxPoint& aStart, const wxPoint& aEnd, int aThickness )
  69. {
  70. PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::SEGMENT );
  71. item->SetFilled( false );
  72. item->SetStart( aStart );
  73. item->SetEnd( aEnd );
  74. item->SetWidth( aThickness );
  75. item->SetParent( this );
  76. m_editPrimitives.emplace_back( item );
  77. SetDirty();
  78. }
  79. void PAD::AddPrimitiveArc( const wxPoint& aCenter, const wxPoint& aStart, int aArcAngle,
  80. int aThickness )
  81. {
  82. PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::ARC );
  83. item->SetFilled( false );
  84. item->SetCenter( aCenter );
  85. item->SetStart( aStart );
  86. item->SetArcAngleAndEnd( aArcAngle );
  87. item->SetWidth( aThickness );
  88. item->SetParent( this );
  89. m_editPrimitives.emplace_back( item );
  90. SetDirty();
  91. }
  92. void PAD::AddPrimitiveCurve( const wxPoint& aStart, const wxPoint& aEnd, const wxPoint& aCtrl1,
  93. const wxPoint& aCtrl2, int aThickness )
  94. {
  95. PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::BEZIER );
  96. item->SetFilled( false );
  97. item->SetStart( aStart );
  98. item->SetEnd( aEnd );
  99. item->SetBezierC1( aCtrl1 );
  100. item->SetBezierC2( aCtrl2 );
  101. item->SetWidth( aThickness );
  102. item->SetParent( this );
  103. m_editPrimitives.emplace_back( item );
  104. SetDirty();
  105. }
  106. void PAD::AddPrimitiveCircle( const wxPoint& aCenter, int aRadius, int aThickness, bool aFilled )
  107. {
  108. PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::CIRCLE );
  109. item->SetFilled( aFilled );
  110. item->SetStart( aCenter );
  111. item->SetEnd( wxPoint( aCenter.x + aRadius, aCenter.y ) );
  112. item->SetWidth( aThickness );
  113. item->SetParent( this );
  114. m_editPrimitives.emplace_back( item );
  115. SetDirty();
  116. }
  117. void PAD::AddPrimitiveRect( const wxPoint& aStart, const wxPoint& aEnd, int aThickness,
  118. bool aFilled)
  119. {
  120. PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T:: RECT );
  121. item->SetFilled( aFilled );
  122. item->SetStart( aStart );
  123. item->SetEnd( aEnd );
  124. item->SetWidth( aThickness );
  125. item->SetParent( this );
  126. m_editPrimitives.emplace_back( item );
  127. SetDirty();
  128. }
  129. void PAD::ReplacePrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList )
  130. {
  131. // clear old list
  132. DeletePrimitivesList();
  133. // Import to the given shape list
  134. if( aPrimitivesList.size() )
  135. AppendPrimitives( aPrimitivesList );
  136. SetDirty();
  137. }
  138. void PAD::AppendPrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList )
  139. {
  140. // Add duplicates of aPrimitivesList to the pad primitives list:
  141. for( const std::shared_ptr<PCB_SHAPE>& prim : aPrimitivesList )
  142. AddPrimitive( new PCB_SHAPE( *prim ) );
  143. SetDirty();
  144. }
  145. void PAD::AddPrimitive( PCB_SHAPE* aPrimitive )
  146. {
  147. aPrimitive->SetParent( this );
  148. m_editPrimitives.emplace_back( aPrimitive );
  149. SetDirty();
  150. }
  151. // clear the basic shapes list and associated data
  152. void PAD::DeletePrimitivesList()
  153. {
  154. m_editPrimitives.clear();
  155. SetDirty();
  156. }
  157. void PAD::addPadPrimitivesToPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError,
  158. ERROR_LOC aErrorLoc ) const
  159. {
  160. SHAPE_POLY_SET polyset;
  161. for( const std::shared_ptr<PCB_SHAPE>& primitive : m_editPrimitives )
  162. {
  163. primitive->TransformShapeWithClearanceToPolygon( polyset, UNDEFINED_LAYER, 0, aError,
  164. aErrorLoc );
  165. }
  166. polyset.Simplify( SHAPE_POLY_SET::PM_FAST );
  167. // Merge all polygons with the initial pad anchor shape
  168. if( polyset.OutlineCount() )
  169. {
  170. aMergedPolygon->BooleanAdd( polyset, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  171. aMergedPolygon->Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  172. }
  173. }
  174. void PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon, ERROR_LOC aErrorLoc ) const
  175. {
  176. const BOARD* board = GetBoard();
  177. int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF;
  178. aMergedPolygon->RemoveAllContours();
  179. // Add the anchor pad shape in aMergedPolygon, others in aux_polyset:
  180. // The anchor pad is always at 0,0
  181. switch( GetAnchorPadShape() )
  182. {
  183. case PAD_SHAPE::RECT:
  184. {
  185. SHAPE_RECT rect( -GetSize().x / 2, -GetSize().y / 2, GetSize().x, GetSize().y );
  186. aMergedPolygon->AddOutline( rect.Outline() );
  187. }
  188. break;
  189. default:
  190. case PAD_SHAPE::CIRCLE:
  191. TransformCircleToPolygon( *aMergedPolygon, wxPoint( 0, 0 ), GetSize().x / 2, maxError,
  192. aErrorLoc );
  193. break;
  194. }
  195. addPadPrimitivesToPolygon( aMergedPolygon, maxError, aErrorLoc );
  196. }
  197. bool PAD::GetBestAnchorPosition( VECTOR2I& aPos )
  198. {
  199. SHAPE_POLY_SET poly;
  200. addPadPrimitivesToPolygon( &poly, ARC_LOW_DEF, ERROR_INSIDE );
  201. if( poly.OutlineCount() > 1 )
  202. return false;
  203. const int minSteps = 10;
  204. const int maxSteps = 50;
  205. int stepsX, stepsY;
  206. auto bbox = poly.BBox();
  207. if( bbox.GetWidth() < bbox.GetHeight() )
  208. {
  209. stepsX = minSteps;
  210. stepsY = minSteps * (double) bbox.GetHeight() / (double )(bbox.GetWidth() + 1);
  211. }
  212. else
  213. {
  214. stepsY = minSteps;
  215. stepsX = minSteps * (double) bbox.GetWidth() / (double )(bbox.GetHeight() + 1);
  216. }
  217. stepsX = std::max(minSteps, std::min( maxSteps, stepsX ) );
  218. stepsY = std::max(minSteps, std::min( maxSteps, stepsY ) );
  219. VECTOR2I center = bbox.Centre();
  220. int64_t minDist = std::numeric_limits<int64_t>::max();
  221. int64_t minDistEdge;
  222. if( GetAnchorPadShape() == PAD_SHAPE::CIRCLE )
  223. {
  224. minDistEdge = GetSize().x;
  225. }
  226. else
  227. {
  228. minDistEdge = std::max( GetSize().x, GetSize().y );
  229. }
  230. OPT<VECTOR2I> bestAnchor( []()->OPT<VECTOR2I> { return NULLOPT; }() );
  231. for( int y = 0; y < stepsY ; y++ )
  232. {
  233. for( int x = 0; x < stepsX; x++ )
  234. {
  235. VECTOR2I p = bbox.GetPosition();
  236. p.x += rescale( x, bbox.GetWidth(), (stepsX - 1) );
  237. p.y += rescale( y, bbox.GetHeight(), (stepsY - 1) );
  238. if( poly.Contains(p) )
  239. {
  240. int dist = (center - p).EuclideanNorm();
  241. int distEdge = poly.COutline(0).Distance( p, true );
  242. if( distEdge >= minDistEdge )
  243. {
  244. if( dist < minDist )
  245. {
  246. bestAnchor = p;
  247. minDist = dist;
  248. }
  249. }
  250. }
  251. }
  252. }
  253. if( bestAnchor )
  254. {
  255. aPos = *bestAnchor;
  256. return true;
  257. }
  258. return false;
  259. }