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.

1433 lines
44 KiB

14 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
14 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
14 years ago
14 years ago
5 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
5 years ago
5 years ago
18 years ago
18 years ago
18 years ago
5 years ago
18 years ago
14 years ago
18 years ago
18 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
Introduction of Graphics Abstraction Layer based rendering for pcbnew. New classes: - VIEW - represents view that is seen by user, takes care of layer ordering & visibility and how it is displayed (which location, how much zoomed, etc.) - VIEW_ITEM - Base class for every item that can be displayed on VIEW (the biggest change is that now it may be necessary to override ViewBBox & ViewGetLayers method for derived classes). - EDA_DRAW_PANEL_GAL - Inherits after EDA_DRAW_PANEL, displays VIEW output, right now it is not editable (in opposite to usual EDA_DRAW_PANEL). - GAL/OPENGL_GAL/CAIRO_GAL - Base Graphics Abstraction Layer class + two different flavours (Cairo is not fully supported yet), that offers methods to draw primitives using different libraries. - WX_VIEW_CONTROLS - Controller for VIEW, handles user events, allows zooming, panning, etc. - PAINTER/PCB_PAINTER - Classes that uses GAL interface to draw items (as you may have already guessed - PCB_PAINTER is a class for drawing PCB specific object, PAINTER is an abstract class). Its methods are invoked by VIEW, when an item has to be drawn. To display a new type of item - you need to implement draw(ITEM_TYPE*) method that draws it using GAL methods. - STROKE_FONT - Implements stroke font drawing using GAL methods. Most important changes to Kicad original code: * EDA_ITEM now inherits from VIEW_ITEM, which is a base class for all drawable objects. * EDA_DRAW_FRAME contains both usual EDA_DRAW_PANEL and new EDA_DRAW_PANEL_GAL, that can be switched anytime. * There are some new layers for displaying multilayer pads, vias & pads holes (these are not shown yet on the right sidebar in pcbnew) * Display order of layers is different than in previous versions (if you are curious - you may check m_galLayerOrder@pcbnew/basepcbframe.cpp). Preserving usual order would result in not very natural display, such as showing silkscreen texts on the bottom. * Introduced new hotkey (Alt+F12) and new menu option (View->Switch canvas) for switching canvas during runtime. * Some of classes (mostly derived from BOARD_ITEM) now includes ViewBBox & ViewGetLayers methods. * Removed tools/class_painter.h, as now it is extended and included in source code. Build changes: * GAL-based rendering option is turned on by a new compilation CMake option KICAD_GAL. * When compiling with CMake option KICAD_GAL=ON, GLEW and Cairo libraries are required. * GAL-related code is compiled into a static library (common/libgal). * Build with KICAD_GAL=OFF should not need any new libraries and should come out as a standard version of Kicad Currently most of items in pcbnew can be displayed using OpenGL (to be done are DIMENSIONS and MARKERS). More details about GAL can be found in: http://www.ohwr.org/attachments/1884/view-spec.pdf
13 years ago
Introduction of Graphics Abstraction Layer based rendering for pcbnew. New classes: - VIEW - represents view that is seen by user, takes care of layer ordering & visibility and how it is displayed (which location, how much zoomed, etc.) - VIEW_ITEM - Base class for every item that can be displayed on VIEW (the biggest change is that now it may be necessary to override ViewBBox & ViewGetLayers method for derived classes). - EDA_DRAW_PANEL_GAL - Inherits after EDA_DRAW_PANEL, displays VIEW output, right now it is not editable (in opposite to usual EDA_DRAW_PANEL). - GAL/OPENGL_GAL/CAIRO_GAL - Base Graphics Abstraction Layer class + two different flavours (Cairo is not fully supported yet), that offers methods to draw primitives using different libraries. - WX_VIEW_CONTROLS - Controller for VIEW, handles user events, allows zooming, panning, etc. - PAINTER/PCB_PAINTER - Classes that uses GAL interface to draw items (as you may have already guessed - PCB_PAINTER is a class for drawing PCB specific object, PAINTER is an abstract class). Its methods are invoked by VIEW, when an item has to be drawn. To display a new type of item - you need to implement draw(ITEM_TYPE*) method that draws it using GAL methods. - STROKE_FONT - Implements stroke font drawing using GAL methods. Most important changes to Kicad original code: * EDA_ITEM now inherits from VIEW_ITEM, which is a base class for all drawable objects. * EDA_DRAW_FRAME contains both usual EDA_DRAW_PANEL and new EDA_DRAW_PANEL_GAL, that can be switched anytime. * There are some new layers for displaying multilayer pads, vias & pads holes (these are not shown yet on the right sidebar in pcbnew) * Display order of layers is different than in previous versions (if you are curious - you may check m_galLayerOrder@pcbnew/basepcbframe.cpp). Preserving usual order would result in not very natural display, such as showing silkscreen texts on the bottom. * Introduced new hotkey (Alt+F12) and new menu option (View->Switch canvas) for switching canvas during runtime. * Some of classes (mostly derived from BOARD_ITEM) now includes ViewBBox & ViewGetLayers methods. * Removed tools/class_painter.h, as now it is extended and included in source code. Build changes: * GAL-based rendering option is turned on by a new compilation CMake option KICAD_GAL. * When compiling with CMake option KICAD_GAL=ON, GLEW and Cairo libraries are required. * GAL-related code is compiled into a static library (common/libgal). * Build with KICAD_GAL=OFF should not need any new libraries and should come out as a standard version of Kicad Currently most of items in pcbnew can be displayed using OpenGL (to be done are DIMENSIONS and MARKERS). More details about GAL can be found in: http://www.ohwr.org/attachments/1884/view-spec.pdf
13 years ago
Introduction of Graphics Abstraction Layer based rendering for pcbnew. New classes: - VIEW - represents view that is seen by user, takes care of layer ordering & visibility and how it is displayed (which location, how much zoomed, etc.) - VIEW_ITEM - Base class for every item that can be displayed on VIEW (the biggest change is that now it may be necessary to override ViewBBox & ViewGetLayers method for derived classes). - EDA_DRAW_PANEL_GAL - Inherits after EDA_DRAW_PANEL, displays VIEW output, right now it is not editable (in opposite to usual EDA_DRAW_PANEL). - GAL/OPENGL_GAL/CAIRO_GAL - Base Graphics Abstraction Layer class + two different flavours (Cairo is not fully supported yet), that offers methods to draw primitives using different libraries. - WX_VIEW_CONTROLS - Controller for VIEW, handles user events, allows zooming, panning, etc. - PAINTER/PCB_PAINTER - Classes that uses GAL interface to draw items (as you may have already guessed - PCB_PAINTER is a class for drawing PCB specific object, PAINTER is an abstract class). Its methods are invoked by VIEW, when an item has to be drawn. To display a new type of item - you need to implement draw(ITEM_TYPE*) method that draws it using GAL methods. - STROKE_FONT - Implements stroke font drawing using GAL methods. Most important changes to Kicad original code: * EDA_ITEM now inherits from VIEW_ITEM, which is a base class for all drawable objects. * EDA_DRAW_FRAME contains both usual EDA_DRAW_PANEL and new EDA_DRAW_PANEL_GAL, that can be switched anytime. * There are some new layers for displaying multilayer pads, vias & pads holes (these are not shown yet on the right sidebar in pcbnew) * Display order of layers is different than in previous versions (if you are curious - you may check m_galLayerOrder@pcbnew/basepcbframe.cpp). Preserving usual order would result in not very natural display, such as showing silkscreen texts on the bottom. * Introduced new hotkey (Alt+F12) and new menu option (View->Switch canvas) for switching canvas during runtime. * Some of classes (mostly derived from BOARD_ITEM) now includes ViewBBox & ViewGetLayers methods. * Removed tools/class_painter.h, as now it is extended and included in source code. Build changes: * GAL-based rendering option is turned on by a new compilation CMake option KICAD_GAL. * When compiling with CMake option KICAD_GAL=ON, GLEW and Cairo libraries are required. * GAL-related code is compiled into a static library (common/libgal). * Build with KICAD_GAL=OFF should not need any new libraries and should come out as a standard version of Kicad Currently most of items in pcbnew can be displayed using OpenGL (to be done are DIMENSIONS and MARKERS). More details about GAL can be found in: http://www.ohwr.org/attachments/1884/view-spec.pdf
13 years ago
Introduction of Graphics Abstraction Layer based rendering for pcbnew. New classes: - VIEW - represents view that is seen by user, takes care of layer ordering & visibility and how it is displayed (which location, how much zoomed, etc.) - VIEW_ITEM - Base class for every item that can be displayed on VIEW (the biggest change is that now it may be necessary to override ViewBBox & ViewGetLayers method for derived classes). - EDA_DRAW_PANEL_GAL - Inherits after EDA_DRAW_PANEL, displays VIEW output, right now it is not editable (in opposite to usual EDA_DRAW_PANEL). - GAL/OPENGL_GAL/CAIRO_GAL - Base Graphics Abstraction Layer class + two different flavours (Cairo is not fully supported yet), that offers methods to draw primitives using different libraries. - WX_VIEW_CONTROLS - Controller for VIEW, handles user events, allows zooming, panning, etc. - PAINTER/PCB_PAINTER - Classes that uses GAL interface to draw items (as you may have already guessed - PCB_PAINTER is a class for drawing PCB specific object, PAINTER is an abstract class). Its methods are invoked by VIEW, when an item has to be drawn. To display a new type of item - you need to implement draw(ITEM_TYPE*) method that draws it using GAL methods. - STROKE_FONT - Implements stroke font drawing using GAL methods. Most important changes to Kicad original code: * EDA_ITEM now inherits from VIEW_ITEM, which is a base class for all drawable objects. * EDA_DRAW_FRAME contains both usual EDA_DRAW_PANEL and new EDA_DRAW_PANEL_GAL, that can be switched anytime. * There are some new layers for displaying multilayer pads, vias & pads holes (these are not shown yet on the right sidebar in pcbnew) * Display order of layers is different than in previous versions (if you are curious - you may check m_galLayerOrder@pcbnew/basepcbframe.cpp). Preserving usual order would result in not very natural display, such as showing silkscreen texts on the bottom. * Introduced new hotkey (Alt+F12) and new menu option (View->Switch canvas) for switching canvas during runtime. * Some of classes (mostly derived from BOARD_ITEM) now includes ViewBBox & ViewGetLayers methods. * Removed tools/class_painter.h, as now it is extended and included in source code. Build changes: * GAL-based rendering option is turned on by a new compilation CMake option KICAD_GAL. * When compiling with CMake option KICAD_GAL=ON, GLEW and Cairo libraries are required. * GAL-related code is compiled into a static library (common/libgal). * Build with KICAD_GAL=OFF should not need any new libraries and should come out as a standard version of Kicad Currently most of items in pcbnew can be displayed using OpenGL (to be done are DIMENSIONS and MARKERS). More details about GAL can be found in: http://www.ohwr.org/attachments/1884/view-spec.pdf
13 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
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) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  6. * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #include <base_units.h>
  26. #include <bitmaps.h>
  27. #include <math/util.h> // for KiROUND
  28. #include <eda_draw_frame.h>
  29. #include <geometry/shape_circle.h>
  30. #include <geometry/shape_segment.h>
  31. #include <geometry/shape_simple.h>
  32. #include <geometry/shape_rect.h>
  33. #include <geometry/shape_compound.h>
  34. #include <kicad_string.h>
  35. #include <i18n_utility.h>
  36. #include <view/view.h>
  37. #include <board.h>
  38. #include <footprint.h>
  39. #include <pcb_shape.h>
  40. #include <connectivity/connectivity_data.h>
  41. #include <convert_to_biu.h>
  42. #include <convert_basic_shapes_to_polygon.h>
  43. #include <widgets/msgpanel.h>
  44. #include <memory>
  45. PAD::PAD( FOOTPRINT* parent ) :
  46. BOARD_CONNECTED_ITEM( parent, PCB_PAD_T )
  47. {
  48. m_size.x = m_size.y = Mils2iu( 60 ); // Default pad size 60 mils.
  49. m_drill.x = m_drill.y = Mils2iu( 30 ); // Default drill size 30 mils.
  50. m_orient = 0; // Pad rotation in 1/10 degrees.
  51. m_lengthPadToDie = 0;
  52. if( m_parent && m_parent->Type() == PCB_FOOTPRINT_T )
  53. {
  54. m_pos = GetParent()->GetPosition();
  55. }
  56. SetShape( PAD_SHAPE_CIRCLE ); // Default pad shape is PAD_CIRCLE.
  57. SetAnchorPadShape( PAD_SHAPE_CIRCLE ); // Default shape for custom shaped pads
  58. // is PAD_CIRCLE.
  59. SetDrillShape( PAD_DRILL_SHAPE_CIRCLE ); // Default pad drill shape is a circle.
  60. m_attribute = PAD_ATTRIB_PTH; // Default pad type is plated through hole
  61. SetProperty( PAD_PROP_NONE ); // no special fabrication property
  62. m_localClearance = 0;
  63. m_localSolderMaskMargin = 0;
  64. m_localSolderPasteMargin = 0;
  65. m_localSolderPasteMarginRatio = 0.0;
  66. // Parameters for round rect only:
  67. m_roundedCornerScale = 0.25; // from IPC-7351C standard
  68. // Parameters for chamfered rect only:
  69. m_chamferScale = 0.2; // Size of chamfer: ratio of smallest of X,Y size
  70. m_chamferPositions = RECT_NO_CHAMFER; // No chamfered corner
  71. m_zoneConnection = ZONE_CONNECTION::INHERITED; // Use parent setting by default
  72. m_thermalWidth = 0; // Use parent setting by default
  73. m_thermalGap = 0; // Use parent setting by default
  74. m_customShapeClearanceArea = CUST_PAD_SHAPE_IN_ZONE_OUTLINE;
  75. // Set layers mask to default for a standard thru hole pad.
  76. m_layerMask = PTHMask();
  77. SetSubRatsnest( 0 ); // used in ratsnest calculations
  78. m_shapesDirty = true;
  79. m_effectiveBoundingRadius = 0;
  80. m_removeUnconnectedLayer = false;
  81. m_keepTopBottomLayer = true;
  82. }
  83. PAD::PAD( const PAD& aOther ) :
  84. BOARD_CONNECTED_ITEM( aOther.GetParent(), PCB_PAD_T )
  85. {
  86. BOARD_CONNECTED_ITEM::operator=( aOther );
  87. ImportSettingsFrom( aOther );
  88. SetPadToDieLength( aOther.GetPadToDieLength() );
  89. SetPosition( aOther.GetPosition() );
  90. SetPos0( aOther.GetPos0() );
  91. SetName( aOther.GetName() );
  92. SetPinFunction( aOther.GetPinFunction() );
  93. SetSubRatsnest( aOther.GetSubRatsnest() );
  94. m_effectiveBoundingRadius = aOther.m_effectiveBoundingRadius;
  95. m_removeUnconnectedLayer = aOther.m_removeUnconnectedLayer;
  96. m_keepTopBottomLayer = aOther.m_keepTopBottomLayer;
  97. const_cast<KIID&>( m_Uuid ) = aOther.m_Uuid;
  98. }
  99. PAD& PAD::operator=( const PAD &aOther )
  100. {
  101. BOARD_CONNECTED_ITEM::operator=( aOther );
  102. ImportSettingsFrom( aOther );
  103. SetPadToDieLength( aOther.GetPadToDieLength() );
  104. SetPosition( aOther.GetPosition() );
  105. SetPos0( aOther.GetPos0() );
  106. SetName( aOther.GetName() );
  107. SetPinFunction( aOther.GetPinFunction() );
  108. SetSubRatsnest( aOther.GetSubRatsnest() );
  109. m_effectiveBoundingRadius = aOther.m_effectiveBoundingRadius;
  110. m_removeUnconnectedLayer = aOther.m_removeUnconnectedLayer;
  111. m_keepTopBottomLayer = aOther.m_keepTopBottomLayer;
  112. return *this;
  113. }
  114. LSET PAD::PTHMask()
  115. {
  116. static LSET saved = LSET::AllCuMask() | LSET( 2, F_Mask, B_Mask );
  117. return saved;
  118. }
  119. LSET PAD::SMDMask()
  120. {
  121. static LSET saved( 3, F_Cu, F_Paste, F_Mask );
  122. return saved;
  123. }
  124. LSET PAD::ConnSMDMask()
  125. {
  126. static LSET saved( 2, F_Cu, F_Mask );
  127. return saved;
  128. }
  129. LSET PAD::UnplatedHoleMask()
  130. {
  131. static LSET saved = LSET( 4, F_Cu, B_Cu, F_Mask, B_Mask );
  132. return saved;
  133. }
  134. LSET PAD::ApertureMask()
  135. {
  136. static LSET saved( 1, F_Paste );
  137. return saved;
  138. }
  139. bool PAD::IsFlipped() const
  140. {
  141. if( GetParent() && GetParent()->GetLayer() == B_Cu )
  142. return true;
  143. return false;
  144. }
  145. bool PAD::FlashLayer( LSET aLayers ) const
  146. {
  147. for( auto layer : aLayers.Seq() )
  148. {
  149. if( FlashLayer( layer ) )
  150. return true;
  151. }
  152. return false;
  153. }
  154. bool PAD::FlashLayer( int aLayer ) const
  155. {
  156. // Return the "normal" shape if the caller doesn't specify a particular layer
  157. if( aLayer == UNDEFINED_LAYER )
  158. return true;
  159. BOARD* board = GetBoard();
  160. if( !board )
  161. return false;
  162. /// We don't remove the copper from non-PTH pads
  163. if( GetAttribute() != PAD_ATTRIB_PTH )
  164. return IsOnLayer( static_cast<PCB_LAYER_ID>( aLayer ) );
  165. /// Heatsink pads always get copper
  166. if( GetProperty() == PAD_PROP_HEATSINK )
  167. return IsOnLayer( static_cast<PCB_LAYER_ID>( aLayer ) );
  168. if( !m_removeUnconnectedLayer )
  169. return IsOnLayer( static_cast<PCB_LAYER_ID>( aLayer ) );
  170. /// Plated through hole pads need copper on the top/bottom layers for proper soldering
  171. /// Unless the user has removed them in the pad dialog
  172. if( m_keepTopBottomLayer && ( aLayer == F_Cu || aLayer == B_Cu ) )
  173. return IsOnLayer( static_cast<PCB_LAYER_ID>( aLayer ) );
  174. return board->GetConnectivity()->IsConnectedOnLayer( this, static_cast<int>( aLayer ),
  175. { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T } );
  176. }
  177. int PAD::GetRoundRectCornerRadius() const
  178. {
  179. return KiROUND( std::min( m_size.x, m_size.y ) * m_roundedCornerScale );
  180. }
  181. void PAD::SetRoundRectCornerRadius( double aRadius )
  182. {
  183. int min_r = std::min( m_size.x, m_size.y );
  184. if( min_r > 0 )
  185. SetRoundRectRadiusRatio( aRadius / min_r );
  186. }
  187. void PAD::SetRoundRectRadiusRatio( double aRadiusScale )
  188. {
  189. m_roundedCornerScale = std::max( 0.0, std::min( aRadiusScale, 0.5 ) );
  190. m_shapesDirty = true;
  191. }
  192. void PAD::SetChamferRectRatio( double aChamferScale )
  193. {
  194. m_chamferScale = std::max( 0.0, std::min( aChamferScale, 0.5 ) );
  195. m_shapesDirty = true;
  196. }
  197. const std::shared_ptr<SHAPE_POLY_SET>& PAD::GetEffectivePolygon( PCB_LAYER_ID aLayer ) const
  198. {
  199. if( m_shapesDirty )
  200. BuildEffectiveShapes( aLayer );
  201. return m_effectivePolygon;
  202. }
  203. std::shared_ptr<SHAPE> PAD::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
  204. {
  205. if( m_shapesDirty )
  206. BuildEffectiveShapes( aLayer );
  207. return m_effectiveShape;
  208. }
  209. const SHAPE_SEGMENT* PAD::GetEffectiveHoleShape() const
  210. {
  211. if( m_shapesDirty )
  212. BuildEffectiveShapes( UNDEFINED_LAYER );
  213. return m_effectiveHoleShape.get();
  214. }
  215. int PAD::GetBoundingRadius() const
  216. {
  217. if( m_shapesDirty )
  218. BuildEffectiveShapes( UNDEFINED_LAYER );
  219. return m_effectiveBoundingRadius;
  220. }
  221. void PAD::BuildEffectiveShapes( PCB_LAYER_ID aLayer ) const
  222. {
  223. BOARD* board = GetBoard();
  224. int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF;
  225. m_effectiveShape = std::make_shared<SHAPE_COMPOUND>();
  226. m_effectiveHoleShape = nullptr;
  227. auto add = [this]( SHAPE* aShape )
  228. {
  229. m_effectiveShape->AddShape( aShape );
  230. };
  231. wxPoint shapePos = ShapePos(); // Fetch only once; rotation involves trig
  232. PAD_SHAPE_T effectiveShape = GetShape();
  233. if( GetShape() == PAD_SHAPE_CUSTOM )
  234. effectiveShape = GetAnchorPadShape();
  235. switch( effectiveShape )
  236. {
  237. case PAD_SHAPE_CIRCLE:
  238. add( new SHAPE_CIRCLE( shapePos, m_size.x / 2 ) );
  239. break;
  240. case PAD_SHAPE_OVAL:
  241. if( m_size.x == m_size.y ) // the oval pad is in fact a circle
  242. add( new SHAPE_CIRCLE( shapePos, m_size.x / 2 ) );
  243. else
  244. {
  245. wxSize half_size = m_size / 2;
  246. int half_width = std::min( half_size.x, half_size.y );
  247. wxPoint half_len( half_size.x - half_width, half_size.y - half_width );
  248. RotatePoint( &half_len, m_orient );
  249. add( new SHAPE_SEGMENT( shapePos - half_len, shapePos + half_len, half_width * 2 ) );
  250. }
  251. break;
  252. case PAD_SHAPE_RECT:
  253. case PAD_SHAPE_TRAPEZOID:
  254. case PAD_SHAPE_ROUNDRECT:
  255. {
  256. int r = ( effectiveShape == PAD_SHAPE_ROUNDRECT ) ? GetRoundRectCornerRadius() : 0;
  257. wxPoint half_size( m_size.x / 2, m_size.y / 2 );
  258. wxSize trap_delta( 0, 0 );
  259. if( r )
  260. {
  261. half_size -= wxPoint( r, r );
  262. // Avoid degenerated shapes (0 length segments) that always create issues
  263. // For roundrect pad very near a circle, use only a circle
  264. const int min_len = Millimeter2iu( 0.0001);
  265. if( half_size.x < min_len && half_size.y < min_len )
  266. {
  267. add( new SHAPE_CIRCLE( shapePos, r ) );
  268. break;
  269. }
  270. }
  271. else if( effectiveShape == PAD_SHAPE_TRAPEZOID )
  272. {
  273. trap_delta = m_deltaSize / 2;
  274. }
  275. SHAPE_LINE_CHAIN corners;
  276. corners.Append( -half_size.x - trap_delta.y, half_size.y + trap_delta.x );
  277. corners.Append( half_size.x + trap_delta.y, half_size.y - trap_delta.x );
  278. corners.Append( half_size.x - trap_delta.y, -half_size.y + trap_delta.x );
  279. corners.Append( -half_size.x + trap_delta.y, -half_size.y - trap_delta.x );
  280. corners.Rotate( -DECIDEG2RAD( m_orient ) );
  281. corners.Move( shapePos );
  282. // GAL renders rectangles faster than 4-point polygons so it's worth checking if our
  283. // body shape is a rectangle.
  284. if( corners.PointCount() == 4
  285. && corners.CPoint( 0 ).y == corners.CPoint( 1 ).y
  286. && corners.CPoint( 1 ).x == corners.CPoint( 2 ).x
  287. && corners.CPoint( 2 ).y == corners.CPoint( 3 ).y
  288. && corners.CPoint( 4 ).x == corners.CPoint( 0 ).x )
  289. {
  290. int width = std::abs( corners.CPoint( 2 ).x - corners.CPoint( 0 ).x );
  291. int height = std::abs( corners.CPoint( 2 ).y - corners.CPoint( 0 ).y );
  292. VECTOR2I pos( std::min( corners.CPoint( 2 ).x, corners.CPoint( 0 ).x ),
  293. std::min( corners.CPoint( 2 ).y, corners.CPoint( 0 ).y ) );
  294. add( new SHAPE_RECT( pos, width, height ) );
  295. }
  296. else
  297. {
  298. add( new SHAPE_SIMPLE( corners ) );
  299. }
  300. if( r )
  301. {
  302. add( new SHAPE_SEGMENT( corners.CPoint( 0 ), corners.CPoint( 1 ), r * 2 ) );
  303. add( new SHAPE_SEGMENT( corners.CPoint( 1 ), corners.CPoint( 2 ), r * 2 ) );
  304. add( new SHAPE_SEGMENT( corners.CPoint( 2 ), corners.CPoint( 3 ), r * 2 ) );
  305. add( new SHAPE_SEGMENT( corners.CPoint( 3 ), corners.CPoint( 0 ), r * 2 ) );
  306. }
  307. }
  308. break;
  309. case PAD_SHAPE_CHAMFERED_RECT:
  310. {
  311. SHAPE_POLY_SET outline;
  312. TransformRoundChamferedRectToPolygon( outline, shapePos, GetSize(), m_orient,
  313. GetRoundRectCornerRadius(), GetChamferRectRatio(),
  314. GetChamferPositions(), maxError, ERROR_INSIDE );
  315. add( new SHAPE_SIMPLE( outline.COutline( 0 ) ) );
  316. }
  317. break;
  318. default:
  319. wxFAIL_MSG( "PAD::buildEffectiveShapes: Unsupported pad shape: "
  320. + PAD_SHAPE_T_asString( effectiveShape ) );
  321. break;
  322. }
  323. if( GetShape() == PAD_SHAPE_CUSTOM )
  324. {
  325. for( const std::shared_ptr<PCB_SHAPE>& primitive : m_editPrimitives )
  326. {
  327. for( SHAPE* shape : primitive->MakeEffectiveShapes() )
  328. {
  329. shape->Rotate( -DECIDEG2RAD( m_orient ) );
  330. shape->Move( shapePos );
  331. add( shape );
  332. }
  333. }
  334. }
  335. // Polygon
  336. //
  337. m_effectivePolygon = std::make_shared<SHAPE_POLY_SET>();
  338. TransformShapeWithClearanceToPolygon( *m_effectivePolygon, aLayer, 0, maxError, ERROR_INSIDE );
  339. // Bounding box and radius
  340. //
  341. // PADSTACKS TODO: these will both need to cycle through all layers to get the largest
  342. // values....
  343. //
  344. m_effectiveBoundingRadius = 0;
  345. for( int cnt = 0; cnt < m_effectivePolygon->OutlineCount(); ++cnt )
  346. {
  347. const SHAPE_LINE_CHAIN& poly = m_effectivePolygon->COutline( cnt );
  348. for( int ii = 0; ii < poly.PointCount(); ++ii )
  349. {
  350. int dist = KiROUND( ( poly.CPoint( ii ) - m_pos ).EuclideanNorm() );
  351. m_effectiveBoundingRadius = std::max( m_effectiveBoundingRadius, dist );
  352. }
  353. }
  354. m_effectiveBoundingRadius += 1;
  355. BOX2I bbox = m_effectiveShape->BBox();
  356. m_effectiveBoundingBox = EDA_RECT( (wxPoint) bbox.GetPosition(),
  357. wxSize( bbox.GetWidth(), bbox.GetHeight() ) );
  358. // Hole shape
  359. //
  360. wxSize half_size = m_drill / 2;
  361. int half_width = std::min( half_size.x, half_size.y );
  362. wxPoint half_len( half_size.x - half_width, half_size.y - half_width );
  363. RotatePoint( &half_len, m_orient );
  364. m_effectiveHoleShape = std::make_shared<SHAPE_SEGMENT>( m_pos - half_len, m_pos + half_len,
  365. half_width * 2 );
  366. // All done
  367. //
  368. m_shapesDirty = false;
  369. }
  370. const EDA_RECT PAD::GetBoundingBox() const
  371. {
  372. if( m_shapesDirty )
  373. BuildEffectiveShapes( UNDEFINED_LAYER );
  374. return m_effectiveBoundingBox;
  375. }
  376. void PAD::SetDrawCoord()
  377. {
  378. FOOTPRINT* parentFootprint = static_cast<FOOTPRINT*>( m_parent );
  379. m_pos = m_pos0;
  380. if( parentFootprint == NULL )
  381. return;
  382. double angle = parentFootprint->GetOrientation();
  383. RotatePoint( &m_pos.x, &m_pos.y, angle );
  384. m_pos += parentFootprint->GetPosition();
  385. m_shapesDirty = true;
  386. }
  387. void PAD::SetLocalCoord()
  388. {
  389. FOOTPRINT* parentFootprint = static_cast<FOOTPRINT*>( m_parent );
  390. if( parentFootprint == NULL )
  391. {
  392. m_pos0 = m_pos;
  393. return;
  394. }
  395. m_pos0 = m_pos - parentFootprint->GetPosition();
  396. RotatePoint( &m_pos0.x, &m_pos0.y, -parentFootprint->GetOrientation() );
  397. }
  398. void PAD::SetAttribute( PAD_ATTR_T aAttribute )
  399. {
  400. m_attribute = aAttribute;
  401. if( aAttribute == PAD_ATTRIB_SMD )
  402. m_drill = wxSize( 0, 0 );
  403. m_shapesDirty = true;
  404. }
  405. void PAD::SetProperty( PAD_PROP_T aProperty )
  406. {
  407. m_property = aProperty;
  408. m_shapesDirty = true;
  409. }
  410. void PAD::SetOrientation( double aAngle )
  411. {
  412. NORMALIZE_ANGLE_POS( aAngle );
  413. m_orient = aAngle;
  414. m_shapesDirty = true;
  415. }
  416. void PAD::Flip( const wxPoint& aCentre, bool aFlipLeftRight )
  417. {
  418. if( aFlipLeftRight )
  419. {
  420. MIRROR( m_pos.x, aCentre.x );
  421. MIRROR( m_pos0.x, 0 );
  422. MIRROR( m_offset.x, 0 );
  423. MIRROR( m_deltaSize.x, 0 );
  424. }
  425. else
  426. {
  427. MIRROR( m_pos.y, aCentre.y );
  428. MIRROR( m_pos0.y, 0 );
  429. MIRROR( m_offset.y, 0 );
  430. MIRROR( m_deltaSize.y, 0 );
  431. }
  432. SetOrientation( -GetOrientation() );
  433. auto mirrorBitFlags = []( int& aBitfield, int a, int b )
  434. {
  435. bool temp = aBitfield & a;
  436. if( aBitfield & b )
  437. aBitfield |= a;
  438. else
  439. aBitfield &= ~a;
  440. if( temp )
  441. aBitfield |= b;
  442. else
  443. aBitfield &= ~b;
  444. };
  445. if( aFlipLeftRight )
  446. {
  447. mirrorBitFlags( m_chamferPositions, RECT_CHAMFER_TOP_LEFT, RECT_CHAMFER_TOP_RIGHT );
  448. mirrorBitFlags( m_chamferPositions, RECT_CHAMFER_BOTTOM_LEFT, RECT_CHAMFER_BOTTOM_RIGHT );
  449. }
  450. else
  451. {
  452. mirrorBitFlags( m_chamferPositions, RECT_CHAMFER_TOP_LEFT, RECT_CHAMFER_BOTTOM_LEFT );
  453. mirrorBitFlags( m_chamferPositions, RECT_CHAMFER_TOP_RIGHT, RECT_CHAMFER_BOTTOM_RIGHT );
  454. }
  455. // flip pads layers
  456. // PADS items are currently on all copper layers, or
  457. // currently, only on Front or Back layers.
  458. // So the copper layers count is not taken in account
  459. SetLayerSet( FlipLayerMask( m_layerMask ) );
  460. // Flip the basic shapes, in custom pads
  461. FlipPrimitives( aFlipLeftRight );
  462. m_shapesDirty = true;
  463. }
  464. // Flip (mirror) the basic shapes (primitives), in custom pads
  465. void PAD::FlipPrimitives( bool aFlipLeftRight )
  466. {
  467. for( std::shared_ptr<PCB_SHAPE>& primitive : m_editPrimitives )
  468. primitive->Flip( wxPoint( 0, 0 ), aFlipLeftRight );
  469. m_shapesDirty = true;
  470. }
  471. // Returns the position of the pad.
  472. wxPoint PAD::ShapePos() const
  473. {
  474. if( m_offset.x == 0 && m_offset.y == 0 )
  475. return m_pos;
  476. wxPoint loc_offset = m_offset;
  477. RotatePoint( &loc_offset, m_orient );
  478. wxPoint shape_pos = m_pos + loc_offset;
  479. return shape_pos;
  480. }
  481. int PAD::GetLocalClearanceOverrides( wxString* aSource ) const
  482. {
  483. // A pad can have specific clearance that overrides its NETCLASS clearance value
  484. if( GetLocalClearance() )
  485. return GetLocalClearance( aSource );
  486. // A footprint can have a specific clearance value
  487. if( GetParent() && GetParent()->GetLocalClearance() )
  488. return GetParent()->GetLocalClearance( aSource );
  489. return 0;
  490. }
  491. int PAD::GetLocalClearance( wxString* aSource ) const
  492. {
  493. if( aSource )
  494. *aSource = wxString::Format( _( "pad %s" ), GetName() );
  495. return m_localClearance;
  496. }
  497. // Mask margins handling:
  498. int PAD::GetSolderMaskMargin() const
  499. {
  500. // The pad inherits the margin only to calculate a default shape,
  501. // therefore only if it is also a copper layer
  502. // Pads defined only on mask layers (and perhaps on other tech layers) use the shape
  503. // defined by the pad settings only
  504. bool isOnCopperLayer = ( m_layerMask & LSET::AllCuMask() ).any();
  505. if( !isOnCopperLayer )
  506. return 0;
  507. int margin = m_localSolderMaskMargin;
  508. FOOTPRINT* parentFootprint = GetParent();
  509. if( parentFootprint )
  510. {
  511. if( margin == 0 )
  512. {
  513. if( parentFootprint->GetLocalSolderMaskMargin() )
  514. margin = parentFootprint->GetLocalSolderMaskMargin();
  515. }
  516. if( margin == 0 )
  517. {
  518. BOARD* brd = GetBoard();
  519. if( brd )
  520. margin = brd->GetDesignSettings().m_SolderMaskMargin;
  521. }
  522. }
  523. // ensure mask have a size always >= 0
  524. if( margin < 0 )
  525. {
  526. int minsize = -std::min( m_size.x, m_size.y ) / 2;
  527. if( margin < minsize )
  528. margin = minsize;
  529. }
  530. return margin;
  531. }
  532. wxSize PAD::GetSolderPasteMargin() const
  533. {
  534. // The pad inherits the margin only to calculate a default shape,
  535. // therefore only if it is also a copper layer.
  536. // Pads defined only on mask layers (and perhaps on other tech layers) use the shape
  537. // defined by the pad settings only
  538. bool isOnCopperLayer = ( m_layerMask & LSET::AllCuMask() ).any();
  539. if( !isOnCopperLayer )
  540. return wxSize( 0, 0 );
  541. int margin = m_localSolderPasteMargin;
  542. double mratio = m_localSolderPasteMarginRatio;
  543. FOOTPRINT* parentFootprint = GetParent();
  544. if( parentFootprint )
  545. {
  546. if( margin == 0 )
  547. margin = parentFootprint->GetLocalSolderPasteMargin();
  548. auto brd = GetBoard();
  549. if( margin == 0 && brd )
  550. margin = brd->GetDesignSettings().m_SolderPasteMargin;
  551. if( mratio == 0.0 )
  552. mratio = parentFootprint->GetLocalSolderPasteMarginRatio();
  553. if( mratio == 0.0 && brd )
  554. {
  555. mratio = brd->GetDesignSettings().m_SolderPasteMarginRatio;
  556. }
  557. }
  558. wxSize pad_margin;
  559. pad_margin.x = margin + KiROUND( m_size.x * mratio );
  560. pad_margin.y = margin + KiROUND( m_size.y * mratio );
  561. // ensure mask have a size always >= 0
  562. if( pad_margin.x < -m_size.x / 2 )
  563. pad_margin.x = -m_size.x / 2;
  564. if( pad_margin.y < -m_size.y / 2 )
  565. pad_margin.y = -m_size.y / 2;
  566. return pad_margin;
  567. }
  568. ZONE_CONNECTION PAD::GetEffectiveZoneConnection( wxString* aSource ) const
  569. {
  570. FOOTPRINT* parentFootprint = GetParent();
  571. if( m_zoneConnection == ZONE_CONNECTION::INHERITED && parentFootprint )
  572. {
  573. if( aSource )
  574. *aSource = _( "parent footprint" );
  575. return parentFootprint->GetZoneConnection();
  576. }
  577. else
  578. {
  579. if( aSource )
  580. *aSource = _( "pad" );
  581. return m_zoneConnection;
  582. }
  583. }
  584. int PAD::GetEffectiveThermalSpokeWidth( wxString* aSource ) const
  585. {
  586. FOOTPRINT* parentFootprint = GetParent();
  587. if( m_thermalWidth == 0 && parentFootprint )
  588. {
  589. if( aSource )
  590. *aSource = _( "parent footprint" );
  591. return parentFootprint->GetThermalWidth();
  592. }
  593. if( aSource )
  594. *aSource = _( "pad" );
  595. return m_thermalWidth;
  596. }
  597. int PAD::GetEffectiveThermalGap( wxString* aSource ) const
  598. {
  599. FOOTPRINT* parentFootprint = GetParent();
  600. if( m_thermalGap == 0 && parentFootprint )
  601. {
  602. if( aSource )
  603. *aSource = _( "parent footprint" );
  604. return parentFootprint->GetThermalGap();
  605. }
  606. if( aSource )
  607. *aSource = _( "pad" );
  608. return m_thermalGap;
  609. }
  610. void PAD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  611. {
  612. EDA_UNITS units = aFrame->GetUserUnits();
  613. wxString msg, msg2;
  614. BOARD* board = GetBoard();
  615. BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
  616. FOOTPRINT* parentFootprint = static_cast<FOOTPRINT*>( m_parent );
  617. if( parentFootprint )
  618. aList.emplace_back( _( "Footprint" ), parentFootprint->GetReference(), DARKCYAN );
  619. aList.emplace_back( _( "Pad" ), m_name, BROWN );
  620. if( !GetPinFunction().IsEmpty() )
  621. aList.emplace_back( _( "Pin Name" ), GetPinFunction(), BROWN );
  622. aList.emplace_back( _( "Net" ), UnescapeString( GetNetname() ), DARKCYAN );
  623. // Display the netclass name (a pad having a netcode = 0 (no net) use the
  624. // default netclass for clearance):
  625. if( m_netinfo->GetNet() <= 0 )
  626. msg = bds.GetDefault()->GetName();
  627. else
  628. msg = GetNetClassName();
  629. aList.emplace_back( _( "NetClass" ), msg, CYAN );
  630. aList.emplace_back( _( "Layer" ), LayerMaskDescribe(), DARKGREEN );
  631. // Show the pad shape, attribute and property
  632. wxString props = ShowPadAttr();
  633. if( GetProperty() != PAD_PROP_NONE )
  634. props += ',';
  635. switch( GetProperty() )
  636. {
  637. case PAD_PROP_NONE: break;
  638. case PAD_PROP_BGA: props += _("BGA" ); break;
  639. case PAD_PROP_FIDUCIAL_GLBL: props += _("Fiducial global" ); break;
  640. case PAD_PROP_FIDUCIAL_LOCAL: props += _("Fiducial local" ); break;
  641. case PAD_PROP_TESTPOINT: props += _("Test point" ); break;
  642. case PAD_PROP_HEATSINK: props += _("Heat sink" ); break;
  643. case PAD_PROP_CASTELLATED: props += _("Castellated" ); break;
  644. }
  645. aList.emplace_back( ShowPadShape(), props, DARKGREEN );
  646. if( (GetShape() == PAD_SHAPE_CIRCLE || GetShape() == PAD_SHAPE_OVAL )
  647. && m_size.x == m_size.y )
  648. {
  649. msg = MessageTextFromValue( units, m_size.x );
  650. aList.emplace_back( _( "Diameter" ), msg, RED );
  651. }
  652. else
  653. {
  654. msg = MessageTextFromValue( units, m_size.x );
  655. aList.emplace_back( _( "Width" ), msg, RED );
  656. msg = MessageTextFromValue( units, m_size.y );
  657. aList.emplace_back( _( "Height" ), msg, RED );
  658. }
  659. double fp_orient_degrees = parentFootprint ? parentFootprint->GetOrientationDegrees() : 0;
  660. double pad_orient_degrees = GetOrientationDegrees() - fp_orient_degrees;
  661. pad_orient_degrees = NormalizeAngleDegrees( pad_orient_degrees, -180.0, +180.0 );
  662. if( fp_orient_degrees != 0.0 )
  663. msg.Printf( wxT( "%.4g(+ %.4g)" ), pad_orient_degrees, fp_orient_degrees );
  664. else
  665. msg.Printf( wxT( "%.4g" ), GetOrientationDegrees() );
  666. aList.push_back( MSG_PANEL_ITEM( _( "Rotation" ), msg, LIGHTBLUE ) );
  667. if( GetPadToDieLength() )
  668. {
  669. msg = MessageTextFromValue(units, GetPadToDieLength() );
  670. aList.emplace_back( _( "Length in Package" ), msg, CYAN );
  671. }
  672. msg = MessageTextFromValue( units, m_drill.x );
  673. if( GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
  674. {
  675. aList.emplace_back( _( "Drill" ), msg, RED );
  676. }
  677. else
  678. {
  679. msg = MessageTextFromValue( units, m_drill.x )
  680. + wxT( "/" )
  681. + MessageTextFromValue( units, m_drill.y );
  682. aList.emplace_back( _( "Drill X / Y" ), msg, RED );
  683. }
  684. wxString source;
  685. int clearance = GetOwnClearance( GetLayer(), &source );
  686. msg.Printf( _( "Min Clearance: %s" ), MessageTextFromValue( units, clearance ) );
  687. msg2.Printf( _( "(from %s)" ), source );
  688. aList.emplace_back( msg, msg2, BLACK );
  689. }
  690. bool PAD::HitTest( const wxPoint& aPosition, int aAccuracy ) const
  691. {
  692. VECTOR2I delta = aPosition - GetPosition();
  693. int boundingRadius = GetBoundingRadius() + aAccuracy;
  694. if( delta.SquaredEuclideanNorm() > SEG::Square( boundingRadius ) )
  695. return false;
  696. return GetEffectivePolygon()->Contains( aPosition, -1, aAccuracy );
  697. }
  698. bool PAD::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
  699. {
  700. auto getArea = []( const SHAPE_POLY_SET& aPoly ) -> double
  701. {
  702. return aPoly.OutlineCount() ? aPoly.COutline( 0 ).Area() : 0;
  703. };
  704. EDA_RECT arect = aRect;
  705. arect.Normalize();
  706. arect.Inflate( aAccuracy );
  707. EDA_RECT bbox = GetBoundingBox();
  708. if( !arect.Intersects( bbox ) )
  709. return false;
  710. // This covers total containment for all test cases
  711. if( arect.Contains( bbox ) )
  712. return true;
  713. SHAPE_POLY_SET selRect;
  714. selRect.NewOutline();
  715. selRect.Append( arect.GetOrigin() );
  716. selRect.Append( VECTOR2I( arect.GetRight(), arect.GetTop() ) );
  717. selRect.Append( VECTOR2I( arect.GetRight(), arect.GetBottom() ) );
  718. selRect.Append( VECTOR2I( arect.GetLeft(), arect.GetBottom() ) );
  719. selRect.BooleanIntersection( *GetEffectivePolygon(), SHAPE_POLY_SET::PM_FAST );
  720. double padArea = getArea( *GetEffectivePolygon() );
  721. double intersection = getArea( selRect );
  722. if( intersection > ( padArea * 0.99 ) )
  723. return true;
  724. else
  725. return !aContained && intersection > 0;
  726. }
  727. int PAD::Compare( const PAD* padref, const PAD* padcmp )
  728. {
  729. int diff;
  730. if( ( diff = padref->GetShape() - padcmp->GetShape() ) != 0 )
  731. return diff;
  732. if( ( diff = padref->GetDrillShape() - padcmp->GetDrillShape() ) != 0)
  733. return diff;
  734. if( ( diff = padref->m_drill.x - padcmp->m_drill.x ) != 0 )
  735. return diff;
  736. if( ( diff = padref->m_drill.y - padcmp->m_drill.y ) != 0 )
  737. return diff;
  738. if( ( diff = padref->m_size.x - padcmp->m_size.x ) != 0 )
  739. return diff;
  740. if( ( diff = padref->m_size.y - padcmp->m_size.y ) != 0 )
  741. return diff;
  742. if( ( diff = padref->m_offset.x - padcmp->m_offset.x ) != 0 )
  743. return diff;
  744. if( ( diff = padref->m_offset.y - padcmp->m_offset.y ) != 0 )
  745. return diff;
  746. if( ( diff = padref->m_deltaSize.x - padcmp->m_deltaSize.x ) != 0 )
  747. return diff;
  748. if( ( diff = padref->m_deltaSize.y - padcmp->m_deltaSize.y ) != 0 )
  749. return diff;
  750. // TODO: test custom shapes
  751. // Dick: specctra_export needs this
  752. // Lorenzo: gencad also needs it to implement padstacks!
  753. #if __cplusplus >= 201103L
  754. long long d = padref->m_layerMask.to_ullong() - padcmp->m_layerMask.to_ullong();
  755. if( d < 0 )
  756. return -1;
  757. else if( d > 0 )
  758. return 1;
  759. return 0;
  760. #else
  761. // these strings are not typically constructed, since we don't get here often.
  762. std::string s1 = padref->m_layerMask.to_string();
  763. std::string s2 = padcmp->m_layerMask.to_string();
  764. return s1.compare( s2 );
  765. #endif
  766. }
  767. void PAD::Rotate( const wxPoint& aRotCentre, double aAngle )
  768. {
  769. RotatePoint( &m_pos, aRotCentre, aAngle );
  770. m_orient = NormalizeAngle360Min( m_orient + aAngle );
  771. SetLocalCoord();
  772. m_shapesDirty = true;
  773. }
  774. wxString PAD::ShowPadShape() const
  775. {
  776. switch( GetShape() )
  777. {
  778. case PAD_SHAPE_CIRCLE: return _( "Circle" );
  779. case PAD_SHAPE_OVAL: return _( "Oval" );
  780. case PAD_SHAPE_RECT: return _( "Rect" );
  781. case PAD_SHAPE_TRAPEZOID: return _( "Trap" );
  782. case PAD_SHAPE_ROUNDRECT: return _( "Roundrect" );
  783. case PAD_SHAPE_CHAMFERED_RECT: return _( "Chamferedrect" );
  784. case PAD_SHAPE_CUSTOM: return _( "CustomShape" );
  785. default: return wxT( "???" );
  786. }
  787. }
  788. wxString PAD::ShowPadAttr() const
  789. {
  790. switch( GetAttribute() )
  791. {
  792. case PAD_ATTRIB_PTH: return _( "PTH" );
  793. case PAD_ATTRIB_SMD: return _( "SMD" );
  794. case PAD_ATTRIB_CONN: return _( "Conn" );
  795. case PAD_ATTRIB_NPTH: return _( "NPTH" );
  796. default: return wxT( "???" );
  797. }
  798. }
  799. wxString PAD::GetSelectMenuText( EDA_UNITS aUnits ) const
  800. {
  801. if( GetName().IsEmpty() )
  802. {
  803. return wxString::Format( _( "Pad of %s on %s" ),
  804. GetParent()->GetReference(),
  805. LayerMaskDescribe() );
  806. }
  807. else
  808. {
  809. return wxString::Format( _( "Pad %s of %s on %s" ),
  810. GetName(),
  811. GetParent()->GetReference(),
  812. LayerMaskDescribe() );
  813. }
  814. }
  815. BITMAP_DEF PAD::GetMenuImage() const
  816. {
  817. return pad_xpm;
  818. }
  819. EDA_ITEM* PAD::Clone() const
  820. {
  821. return new PAD( *this );
  822. }
  823. bool PAD::PadShouldBeNPTH() const
  824. {
  825. return( m_attribute == PAD_ATTRIB_PTH
  826. && m_drill.x >= m_size.x && m_drill.y >= m_size.y );
  827. }
  828. void PAD::ViewGetLayers( int aLayers[], int& aCount ) const
  829. {
  830. aCount = 0;
  831. // These 2 types of pads contain a hole
  832. if( m_attribute == PAD_ATTRIB_PTH )
  833. aLayers[aCount++] = LAYER_PADS_PLATEDHOLES;
  834. if( m_attribute == PAD_ATTRIB_NPTH )
  835. aLayers[aCount++] = LAYER_NON_PLATEDHOLES;
  836. if( IsOnLayer( F_Cu ) && IsOnLayer( B_Cu ) )
  837. {
  838. // Multi layer pad
  839. aLayers[aCount++] = LAYER_PADS_TH;
  840. aLayers[aCount++] = LAYER_PADS_NETNAMES;
  841. }
  842. else if( IsOnLayer( F_Cu ) )
  843. {
  844. aLayers[aCount++] = LAYER_PAD_FR;
  845. // Is this a PTH pad that has only front copper? If so, we need to also display the
  846. // net name on the PTH netname layer so that it isn't blocked by the drill hole.
  847. if( m_attribute == PAD_ATTRIB_PTH )
  848. aLayers[aCount++] = LAYER_PADS_NETNAMES;
  849. else
  850. aLayers[aCount++] = LAYER_PAD_FR_NETNAMES;
  851. }
  852. else if( IsOnLayer( B_Cu ) )
  853. {
  854. aLayers[aCount++] = LAYER_PAD_BK;
  855. // Is this a PTH pad that has only back copper? If so, we need to also display the
  856. // net name on the PTH netname layer so that it isn't blocked by the drill hole.
  857. if( m_attribute == PAD_ATTRIB_PTH )
  858. aLayers[aCount++] = LAYER_PADS_NETNAMES;
  859. else
  860. aLayers[aCount++] = LAYER_PAD_BK_NETNAMES;
  861. }
  862. else
  863. {
  864. // Internal layers only. (Not yet supported in GUI, but is being used by Python
  865. // footprint generators and will be needed anyway once pad stacks are supported.)
  866. for ( int internal = In1_Cu; internal < In30_Cu; ++internal )
  867. {
  868. if( IsOnLayer( (PCB_LAYER_ID) internal ) )
  869. aLayers[aCount++] = internal;
  870. }
  871. }
  872. // Check non-copper layers. This list should include all the layers that the
  873. // footprint editor allows a pad to be placed on.
  874. static const PCB_LAYER_ID layers_mech[] = { F_Mask, B_Mask, F_Paste, B_Paste,
  875. F_Adhes, B_Adhes, F_SilkS, B_SilkS, Dwgs_User, Eco1_User, Eco2_User };
  876. for( PCB_LAYER_ID each_layer : layers_mech )
  877. {
  878. if( IsOnLayer( each_layer ) )
  879. aLayers[aCount++] = each_layer;
  880. }
  881. #ifdef __WXDEBUG__
  882. if( aCount == 0 ) // Should not occur
  883. {
  884. wxString msg;
  885. msg.Printf( wxT( "footprint %s, pad %s: could not find valid layer for pad" ),
  886. GetParent() ? GetParent()->GetReference() : "<null>",
  887. GetName().IsEmpty() ? "(unnamed)" : GetName() );
  888. wxLogWarning( msg );
  889. }
  890. #endif
  891. }
  892. double PAD::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
  893. {
  894. if( aView->GetPrintMode() > 0 ) // In printing mode the pad is always drawable
  895. return 0.0;
  896. constexpr double HIDE = std::numeric_limits<double>::max();
  897. BOARD* board = GetBoard();
  898. // Meta control for hiding all pads
  899. if( !aView->IsLayerVisible( LAYER_PADS ) )
  900. return HIDE;
  901. // Handle Render tab switches
  902. if( ( GetAttribute() == PAD_ATTRIB_PTH || GetAttribute() == PAD_ATTRIB_NPTH )
  903. && !aView->IsLayerVisible( LAYER_PADS_TH ) )
  904. return HIDE;
  905. if( !IsFlipped() && !aView->IsLayerVisible( LAYER_MOD_FR ) )
  906. return HIDE;
  907. if( IsFlipped() && !aView->IsLayerVisible( LAYER_MOD_BK ) )
  908. return HIDE;
  909. if( IsFrontLayer( (PCB_LAYER_ID) aLayer ) && !aView->IsLayerVisible( LAYER_PAD_FR ) )
  910. return HIDE;
  911. if( IsBackLayer( (PCB_LAYER_ID) aLayer ) && !aView->IsLayerVisible( LAYER_PAD_BK ) )
  912. return HIDE;
  913. if( board )
  914. {
  915. LSET visible = board->GetVisibleLayers() & board->GetEnabledLayers();
  916. // Only draw the pad if at least one of the layers it crosses is being displayed
  917. if( !FlashLayer( visible ) )
  918. return HIDE;
  919. // Don't draw the copper ring of a PTH if none of the copper layers are visible
  920. if( aLayer == LAYER_PADS_TH && ( LSET::AllCuMask() & GetLayerSet() & visible ).none() )
  921. return HIDE;
  922. }
  923. // Netnames will be shown only if zoom is appropriate
  924. if( IsNetnameLayer( aLayer ) )
  925. {
  926. int divisor = std::min( GetBoundingBox().GetWidth(), GetBoundingBox().GetHeight() );
  927. // Pad sizes can be zero briefly when someone is typing a number like "0.5"
  928. // in the pad properties dialog
  929. if( divisor == 0 )
  930. return HIDE;
  931. return ( double ) Millimeter2iu( 5 ) / divisor;
  932. }
  933. // Other layers are shown without any conditions
  934. return 0.0;
  935. }
  936. const BOX2I PAD::ViewBBox() const
  937. {
  938. // Bounding box includes soldermask too. Remember mask and/or paste
  939. // margins can be < 0
  940. int solderMaskMargin = std::max( GetSolderMaskMargin(), 0 );
  941. VECTOR2I solderPasteMargin = VECTOR2D( GetSolderPasteMargin() );
  942. EDA_RECT bbox = GetBoundingBox();
  943. // Look for the biggest possible bounding box
  944. int xMargin = std::max( solderMaskMargin, solderPasteMargin.x );
  945. int yMargin = std::max( solderMaskMargin, solderPasteMargin.y );
  946. return BOX2I( VECTOR2I( bbox.GetOrigin() ) - VECTOR2I( xMargin, yMargin ),
  947. VECTOR2I( bbox.GetSize() ) + VECTOR2I( 2 * xMargin, 2 * yMargin ) );
  948. }
  949. FOOTPRINT* PAD::GetParent() const
  950. {
  951. return dynamic_cast<FOOTPRINT*>( m_parent );
  952. }
  953. void PAD::ImportSettingsFrom( const PAD& aMasterPad )
  954. {
  955. SetShape( aMasterPad.GetShape() );
  956. SetLayerSet( aMasterPad.GetLayerSet() );
  957. SetAttribute( aMasterPad.GetAttribute() );
  958. SetProperty( aMasterPad.GetProperty() );
  959. // I am not sure the m_LengthPadToDie must be imported, because this is
  960. // a parameter really specific to a given pad (JPC).
  961. // So this is currently non imported
  962. #if 0
  963. SetPadToDieLength( aMasterPad.GetPadToDieLength() );
  964. #endif
  965. // The pad orientation, for historical reasons is the
  966. // pad rotation + parent rotation.
  967. // So we have to manage this parent rotation
  968. double pad_rot = aMasterPad.GetOrientation();
  969. if( aMasterPad.GetParent() )
  970. pad_rot -= aMasterPad.GetParent()->GetOrientation();
  971. if( GetParent() )
  972. pad_rot += GetParent()->GetOrientation();
  973. SetOrientation( pad_rot );
  974. SetSize( aMasterPad.GetSize() );
  975. SetDelta( wxSize( 0, 0 ) );
  976. SetOffset( aMasterPad.GetOffset() );
  977. SetDrillSize( aMasterPad.GetDrillSize() );
  978. SetDrillShape( aMasterPad.GetDrillShape() );
  979. SetRoundRectRadiusRatio( aMasterPad.GetRoundRectRadiusRatio() );
  980. SetChamferRectRatio( aMasterPad.GetChamferRectRatio() );
  981. SetChamferPositions( aMasterPad.GetChamferPositions() );
  982. switch( aMasterPad.GetShape() )
  983. {
  984. case PAD_SHAPE_TRAPEZOID:
  985. SetDelta( aMasterPad.GetDelta() );
  986. break;
  987. case PAD_SHAPE_CIRCLE:
  988. // ensure size.y == size.x
  989. SetSize( wxSize( GetSize().x, GetSize().x ) );
  990. break;
  991. default:
  992. ;
  993. }
  994. switch( aMasterPad.GetAttribute() )
  995. {
  996. case PAD_ATTRIB_SMD:
  997. case PAD_ATTRIB_CONN:
  998. // These pads do not have hole (they are expected to be only on one
  999. // external copper layer)
  1000. SetDrillSize( wxSize( 0, 0 ) );
  1001. break;
  1002. default:
  1003. ;
  1004. }
  1005. // copy also local settings:
  1006. SetLocalClearance( aMasterPad.GetLocalClearance() );
  1007. SetLocalSolderMaskMargin( aMasterPad.GetLocalSolderMaskMargin() );
  1008. SetLocalSolderPasteMargin( aMasterPad.GetLocalSolderPasteMargin() );
  1009. SetLocalSolderPasteMarginRatio( aMasterPad.GetLocalSolderPasteMarginRatio() );
  1010. SetZoneConnection( aMasterPad.GetEffectiveZoneConnection() );
  1011. SetThermalSpokeWidth( aMasterPad.GetThermalSpokeWidth() );
  1012. SetThermalGap( aMasterPad.GetThermalGap() );
  1013. SetCustomShapeInZoneOpt( aMasterPad.GetCustomShapeInZoneOpt() );
  1014. // Add or remove custom pad shapes:
  1015. ReplacePrimitives( aMasterPad.GetPrimitives() );
  1016. SetAnchorPadShape( aMasterPad.GetAnchorPadShape() );
  1017. m_shapesDirty = true;
  1018. }
  1019. void PAD::SwapData( BOARD_ITEM* aImage )
  1020. {
  1021. assert( aImage->Type() == PCB_PAD_T );
  1022. std::swap( *((FOOTPRINT*) this), *((FOOTPRINT*) aImage) );
  1023. }
  1024. static struct PAD_DESC
  1025. {
  1026. PAD_DESC()
  1027. {
  1028. ENUM_MAP<PAD_ATTR_T>::Instance()
  1029. .Map( PAD_ATTRIB_PTH, _HKI( "Through-hole" ) )
  1030. .Map( PAD_ATTRIB_SMD, _HKI( "SMD" ) )
  1031. .Map( PAD_ATTRIB_CONN, _HKI( "Edge connector" ) )
  1032. .Map( PAD_ATTRIB_NPTH, _HKI( "NPTH, mechanical" ) );
  1033. ENUM_MAP<PAD_SHAPE_T>::Instance()
  1034. .Map( PAD_SHAPE_CIRCLE, _HKI( "Circle" ) )
  1035. .Map( PAD_SHAPE_RECT, _HKI( "Rectangle" ) )
  1036. .Map( PAD_SHAPE_OVAL, _HKI( "Oval" ) )
  1037. .Map( PAD_SHAPE_TRAPEZOID, _HKI( "Trapezoid" ) )
  1038. .Map( PAD_SHAPE_ROUNDRECT, _HKI( "Rounded rectangle" ) )
  1039. .Map( PAD_SHAPE_CHAMFERED_RECT, _HKI( "Chamfered rectangle" ) )
  1040. .Map( PAD_SHAPE_CUSTOM, _HKI( "Custom" ) );
  1041. ENUM_MAP<PAD_PROP_T>::Instance()
  1042. .Map( PAD_PROP_NONE, _HKI( "None" ) )
  1043. .Map( PAD_PROP_BGA, _HKI( "BGA pad" ) )
  1044. .Map( PAD_PROP_FIDUCIAL_GLBL, _HKI( "Fiducial, global to board" ) )
  1045. .Map( PAD_PROP_FIDUCIAL_LOCAL, _HKI( "Fiducial, local to footprint" ) )
  1046. .Map( PAD_PROP_TESTPOINT, _HKI( "Test point pad" ) )
  1047. .Map( PAD_PROP_HEATSINK, _HKI( "Heatsink pad" ) )
  1048. .Map( PAD_PROP_CASTELLATED, _HKI( "Castellated pad" ) );
  1049. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1050. REGISTER_TYPE( PAD );
  1051. propMgr.InheritsAfter( TYPE_HASH( PAD ), TYPE_HASH( BOARD_CONNECTED_ITEM ) );
  1052. auto padType = new PROPERTY_ENUM<PAD, PAD_ATTR_T>( _HKI( "Pad Type" ),
  1053. &PAD::SetAttribute, &PAD::GetAttribute );
  1054. propMgr.AddProperty( padType );
  1055. auto shape = new PROPERTY_ENUM<PAD, PAD_SHAPE_T>( _HKI( "Shape" ),
  1056. &PAD::SetShape, &PAD::GetShape );
  1057. propMgr.AddProperty( shape );
  1058. propMgr.AddProperty( new PROPERTY<PAD, wxString>( _HKI( "Pad Number" ),
  1059. &PAD::SetName, &PAD::GetName ) );
  1060. propMgr.AddProperty( new PROPERTY<PAD, double>( _HKI( "Orientation" ),
  1061. &PAD::SetOrientationDegrees, &PAD::GetOrientationDegrees,
  1062. PROPERTY_DISPLAY::DEGREE ) );
  1063. propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Size X" ),
  1064. &PAD::SetSizeX, &PAD::GetSizeX,
  1065. PROPERTY_DISPLAY::DISTANCE ) );
  1066. propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Size Y" ),
  1067. &PAD::SetSizeY, &PAD::GetSizeY,
  1068. PROPERTY_DISPLAY::DISTANCE ) );
  1069. propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Hole Size X" ),
  1070. &PAD::SetDrillSizeX, &PAD::GetDrillSizeX,
  1071. PROPERTY_DISPLAY::DISTANCE ) );
  1072. propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Hole Size Y" ),
  1073. &PAD::SetDrillSizeY, &PAD::GetDrillSizeY,
  1074. PROPERTY_DISPLAY::DISTANCE ) );
  1075. propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Pad To Die Length" ),
  1076. &PAD::SetPadToDieLength, &PAD::GetPadToDieLength,
  1077. PROPERTY_DISPLAY::DISTANCE ) );
  1078. propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Local Soldermask Margin" ),
  1079. &PAD::SetLocalSolderMaskMargin, &PAD::GetLocalSolderMaskMargin,
  1080. PROPERTY_DISPLAY::DISTANCE ) );
  1081. propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Local Solderpaste Margin" ),
  1082. &PAD::SetLocalSolderPasteMargin, &PAD::GetLocalSolderPasteMargin,
  1083. PROPERTY_DISPLAY::DISTANCE ) );
  1084. propMgr.AddProperty( new PROPERTY<PAD, double>( _HKI( "Local Solderpaste Margin Ratio" ),
  1085. &PAD::SetLocalSolderPasteMarginRatio, &PAD::GetLocalSolderPasteMarginRatio ) );
  1086. propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Thermal Relief Spoke Width" ),
  1087. &PAD::SetThermalSpokeWidth, &PAD::GetThermalSpokeWidth,
  1088. PROPERTY_DISPLAY::DISTANCE ) );
  1089. propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Thermal Relief" ),
  1090. &PAD::SetThermalGap, &PAD::GetThermalGap,
  1091. PROPERTY_DISPLAY::DISTANCE ) );
  1092. propMgr.AddProperty( new PROPERTY_ENUM<PAD, PAD_PROP_T>( _HKI( "Fabrication Property" ),
  1093. &PAD::SetProperty, &PAD::GetProperty ) );
  1094. auto roundRadiusRatio = new PROPERTY<PAD, double>( _HKI( "Round Radius Ratio" ),
  1095. &PAD::SetRoundRectRadiusRatio, &PAD::GetRoundRectRadiusRatio );
  1096. roundRadiusRatio->SetAvailableFunc(
  1097. [=]( INSPECTABLE* aItem ) -> bool
  1098. {
  1099. return aItem->Get( shape ) == PAD_SHAPE_ROUNDRECT;
  1100. } );
  1101. propMgr.AddProperty( roundRadiusRatio );
  1102. propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Local Clearance" ),
  1103. &PAD::SetLocalClearance, &PAD::GetLocalClearance,
  1104. PROPERTY_DISPLAY::DISTANCE ) );
  1105. // TODO delta, dirill shape offset, layerset, zone connection
  1106. }
  1107. } _PAD_DESC;
  1108. ENUM_TO_WXANY( PAD_SHAPE_T );
  1109. ENUM_TO_WXANY( PAD_PROP_T );