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.

461 lines
16 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2021 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2023 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 <confirm.h>
  25. #include <board_design_settings.h>
  26. #include <pcb_track.h>
  27. #include <pad.h>
  28. #include <zone_filler.h>
  29. #include <board_commit.h>
  30. #include <connectivity/connectivity_data.h>
  31. #include <teardrop/teardrop.h>
  32. #include <drc/drc_rtree.h>
  33. #include <geometry/shape_line_chain.h>
  34. #include <geometry/rtree.h>
  35. #include <convert_basic_shapes_to_polygon.h>
  36. #include <bezier_curves.h>
  37. #include <wx/log.h>
  38. // The first priority level of a teardrop area (arbitrary value)
  39. #define MAGIC_TEARDROP_ZONE_ID 30000
  40. TEARDROP_MANAGER::TEARDROP_MANAGER( BOARD* aBoard, TOOL_MANAGER* aToolManager ) :
  41. m_board( aBoard ),
  42. m_toolManager( aToolManager )
  43. {
  44. m_prmsList = m_board->GetDesignSettings().GetTeadropParamsList();
  45. m_tolerance = 0;
  46. }
  47. ZONE* TEARDROP_MANAGER::createTeardrop( TEARDROP_VARIANT aTeardropVariant,
  48. std::vector<VECTOR2I>& aPoints, PCB_TRACK* aTrack ) const
  49. {
  50. ZONE* teardrop = new ZONE( m_board );
  51. // teardrop settings are the last zone settings used by a zone dialog.
  52. // override them by default.
  53. ZONE_SETTINGS::GetDefaultSettings().ExportSetting( *teardrop, false );
  54. // Add zone properties (priority will be fixed later)
  55. teardrop->SetTeardropAreaType( aTeardropVariant == TD_TYPE_PADVIA ? TEARDROP_TYPE::TD_VIAPAD
  56. : TEARDROP_TYPE::TD_TRACKEND );
  57. teardrop->SetLayer( aTrack->GetLayer() );
  58. teardrop->SetNetCode( aTrack->GetNetCode() );
  59. teardrop->SetLocalClearance( 0 );
  60. teardrop->SetMinThickness( pcbIUScale.mmToIU( 0.0254 ) ); // The minimum zone thickness
  61. teardrop->SetPadConnection( ZONE_CONNECTION::FULL );
  62. teardrop->SetIsFilled( false );
  63. teardrop->SetZoneName( aTeardropVariant == TD_TYPE_PADVIA ? MAGIC_TEARDROP_PADVIA_NAME
  64. : MAGIC_TEARDROP_TRACK_NAME );
  65. teardrop->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::NEVER );
  66. teardrop->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL,
  67. pcbIUScale.mmToIU( 0.1 ), true );
  68. SHAPE_POLY_SET* outline = teardrop->Outline();
  69. outline->NewOutline();
  70. for( const VECTOR2I& pt: aPoints )
  71. outline->Append( pt.x, pt.y );
  72. // Until we know better (ie: pay for a potentially very expensive zone refill), the teardrop
  73. // fill is the same as its outline.
  74. teardrop->SetFilledPolysList( aTrack->GetLayer(), *teardrop->Outline() );
  75. teardrop->SetIsFilled( true );
  76. // Used in priority calculations:
  77. teardrop->CalculateFilledArea();
  78. return teardrop;
  79. }
  80. void TEARDROP_MANAGER::RemoveTeardrops( BOARD_COMMIT& aCommit,
  81. const std::vector<BOARD_ITEM*>* dirtyPadsAndVias,
  82. const std::set<PCB_TRACK*>* dirtyTracks )
  83. {
  84. std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
  85. std::vector<ZONE*> stale_teardrops;
  86. for( ZONE* zone : m_board->Zones() )
  87. {
  88. if( zone->IsTeardropArea() )
  89. {
  90. bool stale = false;
  91. std::vector<PAD*> connectedPads;
  92. std::vector<PCB_VIA*> connectedVias;
  93. connectivity->GetConnectedPadsAndVias( zone, &connectedPads, &connectedVias );
  94. for( PAD* pad : connectedPads )
  95. {
  96. if( alg::contains( *dirtyPadsAndVias, pad ) )
  97. {
  98. stale = true;
  99. break;
  100. }
  101. }
  102. if( !stale )
  103. {
  104. for( PCB_VIA* via : connectedVias )
  105. {
  106. if( alg::contains( *dirtyPadsAndVias, via ) )
  107. {
  108. stale = true;
  109. break;
  110. }
  111. }
  112. }
  113. if( stale )
  114. stale_teardrops.push_back( zone );
  115. }
  116. }
  117. for( ZONE* td : stale_teardrops )
  118. {
  119. m_board->Remove( td, REMOVE_MODE::BULK );
  120. aCommit.Removed( td );
  121. }
  122. }
  123. void TEARDROP_MANAGER::UpdateTeardrops( BOARD_COMMIT& aCommit,
  124. const std::vector<BOARD_ITEM*>* dirtyPadsAndVias,
  125. const std::set<PCB_TRACK*>* dirtyTracks,
  126. bool aForceFullUpdate )
  127. {
  128. if( m_board->LegacyTeardrops() )
  129. return;
  130. // Init parameters:
  131. m_tolerance = pcbIUScale.mmToIU( 0.01 );
  132. buildTrackCaches();
  133. // Old teardrops must be removed, to ensure a clean teardrop rebuild
  134. if( aForceFullUpdate )
  135. {
  136. std::vector<ZONE*> teardrops;
  137. for( ZONE* zone : m_board->Zones() )
  138. {
  139. if( zone->IsTeardropArea() )
  140. teardrops.push_back( zone );
  141. }
  142. for( ZONE* td : teardrops )
  143. {
  144. m_board->Remove( td, REMOVE_MODE::BULK );
  145. aCommit.Removed( td );
  146. }
  147. }
  148. std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
  149. for( PCB_TRACK* track : m_board->Tracks() )
  150. {
  151. if( ! ( track->Type() == PCB_TRACE_T || track->Type() == PCB_ARC_T ) )
  152. continue;
  153. std::vector<PAD*> connectedPads;
  154. std::vector<PCB_VIA*> connectedVias;
  155. connectivity->GetConnectedPadsAndVias( track, &connectedPads, &connectedVias );
  156. bool forceUpdate = aForceFullUpdate || alg::contains( *dirtyTracks, track );
  157. for( PAD* pad : connectedPads )
  158. {
  159. if( !forceUpdate && !alg::contains( *dirtyPadsAndVias, pad ) )
  160. continue;
  161. if( pad->GetShape() == PAD_SHAPE::CUSTOM )
  162. // A teardrop shape cannot be built
  163. continue;
  164. TEARDROP_PARAMETERS& tdParams = pad->GetTeardropParams();
  165. int annularWidth = std::min( pad->GetSize().x, pad->GetSize().y );
  166. if( !tdParams.m_Enabled )
  167. continue;
  168. // Ensure a teardrop shape can be built: track width must be < teardrop width and
  169. // filter width
  170. if( track->GetWidth() >= tdParams.m_TdMaxWidth
  171. || track->GetWidth() >= annularWidth * tdParams.m_BestWidthRatio
  172. || track->GetWidth() >= annularWidth * tdParams.m_WidthtoSizeFilterRatio )
  173. {
  174. continue;
  175. }
  176. if( pad->HitTest( track->GetStart() ) && pad->HitTest( track->GetEnd() ) )
  177. // The track is entirely inside the pad; cannot create a teardrop
  178. continue;
  179. // Skip case where pad and the track are within a copper zone with the same net
  180. // (and the pad can be connected to the zone)
  181. if( !tdParams.m_TdOnPadsInZones && areItemsInSameZone( pad, track ) )
  182. continue;
  183. std::vector<VECTOR2I> points;
  184. if( computeTeardropPolygon( tdParams, points, track, pad, pad->GetPosition() ) )
  185. {
  186. ZONE* new_teardrop = createTeardrop( TD_TYPE_PADVIA, points, track );
  187. m_board->Add( new_teardrop, ADD_MODE::BULK_INSERT );
  188. m_createdTdList.push_back( new_teardrop );
  189. aCommit.Added( new_teardrop );
  190. }
  191. }
  192. for( PCB_VIA* via : connectedVias )
  193. {
  194. if( !forceUpdate && !alg::contains( *dirtyPadsAndVias, via ) )
  195. continue;
  196. TEARDROP_PARAMETERS tdParams = via->GetTeardropParams();
  197. int annularWidth = via->GetWidth();
  198. if( !tdParams.m_Enabled )
  199. continue;
  200. // Ensure a teardrop shape can be built: track width must be < teardrop width and
  201. // filter width
  202. if( track->GetWidth() >= tdParams.m_TdMaxWidth
  203. || track->GetWidth() >= annularWidth * tdParams.m_BestWidthRatio
  204. || track->GetWidth() >= annularWidth * tdParams.m_WidthtoSizeFilterRatio )
  205. {
  206. continue;
  207. }
  208. if( via->HitTest( track->GetStart() ) && via->HitTest( track->GetEnd() ) )
  209. // The track is entirely inside the via; cannot create a teardrop
  210. continue;
  211. std::vector<VECTOR2I> points;
  212. if( computeTeardropPolygon( tdParams, points, track, via, via->GetPosition() ) )
  213. {
  214. ZONE* new_teardrop = createTeardrop( TD_TYPE_PADVIA, points, track );
  215. m_board->Add( new_teardrop, ADD_MODE::BULK_INSERT );
  216. m_createdTdList.push_back( new_teardrop );
  217. aCommit.Added( new_teardrop );
  218. }
  219. }
  220. }
  221. if( ( aForceFullUpdate || !dirtyTracks->empty() )
  222. && m_prmsList->GetParameters( TARGET_TRACK )->m_Enabled )
  223. {
  224. AddTeardropsOnTracks( aCommit, dirtyTracks, aForceFullUpdate );
  225. }
  226. // Now set priority of teardrops now all teardrops are added
  227. setTeardropPriorities();
  228. }
  229. void TEARDROP_MANAGER::DeleteTrackToTrackTeardrops( BOARD_COMMIT& aCommit )
  230. {
  231. std::vector<ZONE*> stale_teardrops;
  232. for( ZONE* zone : m_board->Zones() )
  233. {
  234. if( zone->IsTeardropArea() && zone->GetTeardropAreaType() == TEARDROP_TYPE::TD_TRACKEND )
  235. stale_teardrops.push_back( zone );
  236. }
  237. for( ZONE* td : stale_teardrops )
  238. {
  239. m_board->Remove( td, REMOVE_MODE::BULK );
  240. aCommit.Removed( td );
  241. }
  242. }
  243. void TEARDROP_MANAGER::setTeardropPriorities()
  244. {
  245. // Note: a teardrop area is on only one layer, so using GetFirstLayer() is OK
  246. // to know the zone layer of a teardrop
  247. int priority_base = MAGIC_TEARDROP_ZONE_ID;
  248. // The sort function to sort by increasing copper layers. Group by layers.
  249. // For same layers sort by decreasing areas
  250. struct
  251. {
  252. bool operator()(ZONE* a, ZONE* b) const
  253. {
  254. if( a->GetFirstLayer() == b->GetFirstLayer() )
  255. return a->GetOutlineArea() > b->GetOutlineArea();
  256. return a->GetFirstLayer() < b->GetFirstLayer();
  257. }
  258. } compareLess;
  259. for( ZONE* td: m_createdTdList )
  260. td->CalculateOutlineArea();
  261. std::sort( m_createdTdList.begin(), m_createdTdList.end(), compareLess );
  262. int curr_layer = -1;
  263. for( ZONE* td: m_createdTdList )
  264. {
  265. if( td->GetFirstLayer() != curr_layer )
  266. {
  267. curr_layer = td->GetFirstLayer();
  268. priority_base = MAGIC_TEARDROP_ZONE_ID;
  269. }
  270. td->SetAssignedPriority( priority_base++ );
  271. }
  272. }
  273. void TEARDROP_MANAGER::AddTeardropsOnTracks( BOARD_COMMIT& aCommit,
  274. const std::set<PCB_TRACK*>* aTracks,
  275. bool aForceFullUpdate )
  276. {
  277. std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
  278. TEARDROP_PARAMETERS params = *m_prmsList->GetParameters( TARGET_TRACK );
  279. // Explore groups (a group is a set of tracks on the same layer and the same net):
  280. for( auto& grp : m_trackLookupList.GetBuffer() )
  281. {
  282. int layer, netcode;
  283. TRACK_BUFFER::GetNetcodeAndLayerFromIndex( grp.first, &layer, &netcode );
  284. std::vector<PCB_TRACK*>* sublist = grp.second;
  285. if( sublist->size() <= 1 ) // We need at least 2 track segments
  286. continue;
  287. // The sort function to sort by increasing track widths
  288. struct
  289. {
  290. bool operator()(PCB_TRACK* a, PCB_TRACK* b) const
  291. { return a->GetWidth() < b->GetWidth(); }
  292. } compareLess;
  293. std::sort( sublist->begin(), sublist->end(), compareLess );
  294. int min_width = sublist->front()->GetWidth();
  295. int max_width = sublist->back()->GetWidth();
  296. // Skip groups having the same track thickness
  297. if( max_width == min_width )
  298. continue;
  299. for( unsigned ii = 0; ii < sublist->size()-1; ii++ )
  300. {
  301. PCB_TRACK* track = (*sublist)[ii];
  302. int track_len = (int) track->GetLength();
  303. bool track_needs_update = aForceFullUpdate || alg::contains( *aTracks, track );
  304. min_width = track->GetWidth();
  305. // to avoid creating a teardrop between 2 tracks having similar widths give a threshold
  306. params.m_WidthtoSizeFilterRatio = std::max( params.m_WidthtoSizeFilterRatio, 0.1 );
  307. const double th = 1.0 / params.m_WidthtoSizeFilterRatio;
  308. min_width = KiROUND( min_width * th );
  309. for( unsigned jj = ii+1; jj < sublist->size(); jj++ )
  310. {
  311. // Search candidates with thickness > curr thickness
  312. PCB_TRACK* candidate = (*sublist)[jj];
  313. if( min_width >= candidate->GetWidth() )
  314. continue;
  315. // Cannot build a teardrop on a too short track segment.
  316. // The min len is > candidate radius
  317. if( track_len <= candidate->GetWidth() /2 )
  318. continue;
  319. // Now test end to end connection:
  320. EDA_ITEM_FLAGS match_points; // to return the end point EDA_ITEM_FLAGS:
  321. // 0, STARTPOINT, ENDPOINT
  322. VECTOR2I pos = candidate->GetStart();
  323. match_points = track->IsPointOnEnds( pos, m_tolerance );
  324. if( !match_points )
  325. {
  326. pos = candidate->GetEnd();
  327. match_points = track->IsPointOnEnds( pos, m_tolerance );
  328. }
  329. if( !match_points )
  330. continue;
  331. if( !track_needs_update && alg::contains( *aTracks, candidate ) )
  332. continue;
  333. // Pads/vias have priority for teardrops; ensure there isn't one at our position
  334. bool existingPadOrVia = false;
  335. std::vector<PAD*> connectedPads;
  336. std::vector<PCB_VIA*> connectedVias;
  337. connectivity->GetConnectedPadsAndVias( track, &connectedPads, &connectedVias );
  338. for( PAD* pad : connectedPads )
  339. {
  340. if( pad->HitTest( pos ) )
  341. existingPadOrVia = true;
  342. }
  343. for( PCB_VIA* via : connectedVias )
  344. {
  345. if( via->HitTest( pos ) )
  346. existingPadOrVia = true;
  347. }
  348. if( existingPadOrVia )
  349. continue;
  350. std::vector<VECTOR2I> points;
  351. if( computeTeardropPolygon( params, points, track, candidate, pos ) )
  352. {
  353. ZONE* new_teardrop = createTeardrop( TD_TYPE_TRACKEND, points, track );
  354. m_board->Add( new_teardrop, ADD_MODE::BULK_INSERT );
  355. m_createdTdList.push_back( new_teardrop );
  356. aCommit.Added( new_teardrop );
  357. }
  358. }
  359. }
  360. }
  361. }