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.

333 lines
9.9 KiB

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-2022 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->SetStroke( STROKE_PARAMS( aThickness, PLOT_DASH_TYPE::SOLID ) );
  53. item->SetParent( this );
  54. m_editPrimitives.emplace_back( item );
  55. }
  56. SetDirty();
  57. }
  58. void PAD::AddPrimitivePoly( const std::vector<VECTOR2I>& 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->SetStroke( STROKE_PARAMS( aThickness, PLOT_DASH_TYPE::SOLID ) );
  64. item->SetParent( this );
  65. m_editPrimitives.emplace_back( item );
  66. SetDirty();
  67. }
  68. void PAD::AddPrimitiveSegment( const VECTOR2I& aStart, const VECTOR2I& 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->SetStroke( STROKE_PARAMS( aThickness, PLOT_DASH_TYPE::SOLID ) );
  75. item->SetParent( this );
  76. m_editPrimitives.emplace_back( item );
  77. SetDirty();
  78. }
  79. void PAD::AddPrimitiveArc( const VECTOR2I& aCenter, const VECTOR2I& aStart,
  80. const EDA_ANGLE& aArcAngle, 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->SetStroke( STROKE_PARAMS( aThickness, PLOT_DASH_TYPE::SOLID ) );
  88. item->SetParent( this );
  89. m_editPrimitives.emplace_back( item );
  90. SetDirty();
  91. }
  92. void PAD::AddPrimitiveCurve( const VECTOR2I& aStart, const VECTOR2I& aEnd, const VECTOR2I& aCtrl1,
  93. const VECTOR2I& 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->SetStroke( STROKE_PARAMS( aThickness, PLOT_DASH_TYPE::SOLID ) );
  102. item->SetParent( this );
  103. m_editPrimitives.emplace_back( item );
  104. SetDirty();
  105. }
  106. void PAD::AddPrimitiveCircle( const VECTOR2I& 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->SetStroke( STROKE_PARAMS( aThickness, PLOT_DASH_TYPE::SOLID ) );
  113. item->SetParent( this );
  114. m_editPrimitives.emplace_back( item );
  115. SetDirty();
  116. }
  117. void PAD::AddPrimitiveRect( const VECTOR2I& aStart, const VECTOR2I& 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->SetStroke( STROKE_PARAMS( aThickness, PLOT_DASH_TYPE::SOLID ) );
  125. item->SetParent( this );
  126. m_editPrimitives.emplace_back( item );
  127. SetDirty();
  128. }
  129. void PAD::AddPrimitiveAnnotationBox( const VECTOR2I& aStart, const VECTOR2I& aEnd )
  130. {
  131. PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::RECT );
  132. item->SetIsAnnotationProxy();
  133. item->SetFilled( false );
  134. item->SetStart( aStart );
  135. item->SetEnd( aEnd );
  136. item->SetStroke( STROKE_PARAMS( 1, PLOT_DASH_TYPE::SOLID ) );
  137. item->SetParent( this );
  138. m_editPrimitives.emplace_back( item );
  139. SetDirty();
  140. }
  141. void PAD::ReplacePrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList )
  142. {
  143. // clear old list
  144. DeletePrimitivesList();
  145. // Import to the given shape list
  146. if( aPrimitivesList.size() )
  147. AppendPrimitives( aPrimitivesList );
  148. SetDirty();
  149. }
  150. void PAD::AppendPrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList )
  151. {
  152. // Add duplicates of aPrimitivesList to the pad primitives list:
  153. for( const std::shared_ptr<PCB_SHAPE>& prim : aPrimitivesList )
  154. AddPrimitive( new PCB_SHAPE( *prim ) );
  155. SetDirty();
  156. }
  157. void PAD::AddPrimitive( PCB_SHAPE* aPrimitive )
  158. {
  159. aPrimitive->SetParent( this );
  160. m_editPrimitives.emplace_back( aPrimitive );
  161. SetDirty();
  162. }
  163. // clear the basic shapes list and associated data
  164. void PAD::DeletePrimitivesList()
  165. {
  166. m_editPrimitives.clear();
  167. SetDirty();
  168. }
  169. void PAD::addPadPrimitivesToPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError,
  170. ERROR_LOC aErrorLoc ) const
  171. {
  172. SHAPE_POLY_SET polyset;
  173. for( const std::shared_ptr<PCB_SHAPE>& primitive : m_editPrimitives )
  174. {
  175. if( !primitive->IsAnnotationProxy() )
  176. primitive->TransformShapeToPolygon( polyset, UNDEFINED_LAYER, 0, aError, aErrorLoc );
  177. }
  178. polyset.Simplify( SHAPE_POLY_SET::PM_FAST );
  179. // Merge all polygons with the initial pad anchor shape
  180. if( polyset.OutlineCount() )
  181. {
  182. aMergedPolygon->BooleanAdd( polyset, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  183. aMergedPolygon->Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  184. }
  185. }
  186. void PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon, ERROR_LOC aErrorLoc ) const
  187. {
  188. const BOARD* board = GetBoard();
  189. int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF;
  190. aMergedPolygon->RemoveAllContours();
  191. // Add the anchor pad shape in aMergedPolygon, others in aux_polyset:
  192. // The anchor pad is always at 0,0
  193. switch( GetAnchorPadShape() )
  194. {
  195. case PAD_SHAPE::RECT:
  196. {
  197. SHAPE_RECT rect( -GetSize().x / 2, -GetSize().y / 2, GetSize().x, GetSize().y );
  198. aMergedPolygon->AddOutline( rect.Outline() );
  199. }
  200. break;
  201. default:
  202. case PAD_SHAPE::CIRCLE:
  203. TransformCircleToPolygon( *aMergedPolygon, wxPoint( 0, 0 ), GetSize().x / 2, maxError,
  204. aErrorLoc );
  205. break;
  206. }
  207. addPadPrimitivesToPolygon( aMergedPolygon, maxError, aErrorLoc );
  208. }
  209. bool PAD::GetBestAnchorPosition( VECTOR2I& aPos )
  210. {
  211. SHAPE_POLY_SET poly;
  212. addPadPrimitivesToPolygon( &poly, ARC_LOW_DEF, ERROR_INSIDE );
  213. if( poly.OutlineCount() > 1 )
  214. return false;
  215. const int minSteps = 10;
  216. const int maxSteps = 50;
  217. int stepsX, stepsY;
  218. auto bbox = poly.BBox();
  219. if( bbox.GetWidth() < bbox.GetHeight() )
  220. {
  221. stepsX = minSteps;
  222. stepsY = minSteps * (double) bbox.GetHeight() / (double )(bbox.GetWidth() + 1);
  223. }
  224. else
  225. {
  226. stepsY = minSteps;
  227. stepsX = minSteps * (double) bbox.GetWidth() / (double )(bbox.GetHeight() + 1);
  228. }
  229. stepsX = std::max(minSteps, std::min( maxSteps, stepsX ) );
  230. stepsY = std::max(minSteps, std::min( maxSteps, stepsY ) );
  231. VECTOR2I center = bbox.Centre();
  232. int64_t minDist = std::numeric_limits<int64_t>::max();
  233. int64_t minDistEdge;
  234. if( GetAnchorPadShape() == PAD_SHAPE::CIRCLE )
  235. {
  236. minDistEdge = GetSize().x;
  237. }
  238. else
  239. {
  240. minDistEdge = std::max( GetSize().x, GetSize().y );
  241. }
  242. std::optional<VECTOR2I> bestAnchor( []()->std::optional<VECTOR2I> { return std::nullopt; }() );
  243. for( int y = 0; y < stepsY ; y++ )
  244. {
  245. for( int x = 0; x < stepsX; x++ )
  246. {
  247. VECTOR2I p = bbox.GetPosition();
  248. p.x += rescale( x, bbox.GetWidth(), (stepsX - 1) );
  249. p.y += rescale( y, bbox.GetHeight(), (stepsY - 1) );
  250. if( poly.Contains(p) )
  251. {
  252. int dist = (center - p).EuclideanNorm();
  253. int distEdge = poly.COutline(0).Distance( p, true );
  254. if( distEdge >= minDistEdge )
  255. {
  256. if( dist < minDist )
  257. {
  258. bestAnchor = p;
  259. minDist = dist;
  260. }
  261. }
  262. }
  263. }
  264. }
  265. if( bestAnchor )
  266. {
  267. aPos = *bestAnchor;
  268. return true;
  269. }
  270. return false;
  271. }