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.

457 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 || dirtyTracks->contains( track );
  157. for( PAD* pad : connectedPads )
  158. {
  159. if( !forceUpdate && !alg::contains( *dirtyPadsAndVias, pad ) )
  160. continue;
  161. TEARDROP_PARAMETERS& tdParams = pad->GetTeardropParams();
  162. int annularWidth = std::min( pad->GetSize().x, pad->GetSize().y );
  163. if( !tdParams.m_Enabled )
  164. continue;
  165. // Ensure a teardrop shape can be built: track width must be < teardrop width and
  166. // filter width
  167. if( track->GetWidth() >= tdParams.m_TdMaxWidth
  168. || track->GetWidth() >= annularWidth * tdParams.m_BestWidthRatio
  169. || track->GetWidth() >= annularWidth * tdParams.m_WidthtoSizeFilterRatio )
  170. {
  171. continue;
  172. }
  173. if( pad->HitTest( track->GetStart() ) && pad->HitTest( track->GetEnd() ) )
  174. // The track is entirely inside the pad; cannot create a teardrop
  175. continue;
  176. // Skip case where pad and the track are within a copper zone with the same net
  177. // (and the pad can be connected to the zone)
  178. if( !tdParams.m_TdOnPadsInZones && areItemsInSameZone( pad, track ) )
  179. continue;
  180. std::vector<VECTOR2I> points;
  181. if( computeTeardropPolygon( tdParams, points, track, pad, pad->GetPosition() ) )
  182. {
  183. ZONE* new_teardrop = createTeardrop( TD_TYPE_PADVIA, points, track );
  184. m_board->Add( new_teardrop, ADD_MODE::BULK_INSERT );
  185. m_createdTdList.push_back( new_teardrop );
  186. aCommit.Added( new_teardrop );
  187. }
  188. }
  189. for( PCB_VIA* via : connectedVias )
  190. {
  191. if( !forceUpdate && !alg::contains( *dirtyPadsAndVias, via ) )
  192. continue;
  193. TEARDROP_PARAMETERS tdParams = via->GetTeardropParams();
  194. int annularWidth = via->GetWidth();
  195. if( !tdParams.m_Enabled )
  196. continue;
  197. // Ensure a teardrop shape can be built: track width must be < teardrop width and
  198. // filter width
  199. if( track->GetWidth() >= tdParams.m_TdMaxWidth
  200. || track->GetWidth() >= annularWidth * tdParams.m_BestWidthRatio
  201. || track->GetWidth() >= annularWidth * tdParams.m_WidthtoSizeFilterRatio )
  202. {
  203. continue;
  204. }
  205. if( via->HitTest( track->GetStart() ) && via->HitTest( track->GetEnd() ) )
  206. // The track is entirely inside the via; cannot create a teardrop
  207. continue;
  208. std::vector<VECTOR2I> points;
  209. if( computeTeardropPolygon( tdParams, points, track, via, via->GetPosition() ) )
  210. {
  211. ZONE* new_teardrop = createTeardrop( TD_TYPE_PADVIA, points, track );
  212. m_board->Add( new_teardrop, ADD_MODE::BULK_INSERT );
  213. m_createdTdList.push_back( new_teardrop );
  214. aCommit.Added( new_teardrop );
  215. }
  216. }
  217. }
  218. if( ( aForceFullUpdate || !dirtyTracks->empty() )
  219. && m_prmsList->GetParameters( TARGET_TRACK )->m_Enabled )
  220. {
  221. AddTeardropsOnTracks( aCommit, dirtyTracks, aForceFullUpdate );
  222. }
  223. // Now set priority of teardrops now all teardrops are added
  224. setTeardropPriorities();
  225. }
  226. void TEARDROP_MANAGER::DeleteTrackToTrackTeardrops( BOARD_COMMIT& aCommit )
  227. {
  228. std::vector<ZONE*> stale_teardrops;
  229. for( ZONE* zone : m_board->Zones() )
  230. {
  231. if( zone->IsTeardropArea() && zone->GetTeardropAreaType() == TEARDROP_TYPE::TD_TRACKEND )
  232. stale_teardrops.push_back( zone );
  233. }
  234. for( ZONE* td : stale_teardrops )
  235. {
  236. m_board->Remove( td, REMOVE_MODE::BULK );
  237. aCommit.Removed( td );
  238. }
  239. }
  240. void TEARDROP_MANAGER::setTeardropPriorities()
  241. {
  242. // Note: a teardrop area is on only one layer, so using GetFirstLayer() is OK
  243. // to know the zone layer of a teardrop
  244. int priority_base = MAGIC_TEARDROP_ZONE_ID;
  245. // The sort function to sort by increasing copper layers. Group by layers.
  246. // For same layers sort by decreasing areas
  247. struct
  248. {
  249. bool operator()(ZONE* a, ZONE* b) const
  250. {
  251. if( a->GetFirstLayer() == b->GetFirstLayer() )
  252. return a->GetOutlineArea() > b->GetOutlineArea();
  253. return a->GetFirstLayer() < b->GetFirstLayer();
  254. }
  255. } compareLess;
  256. for( ZONE* td: m_createdTdList )
  257. td->CalculateOutlineArea();
  258. std::sort( m_createdTdList.begin(), m_createdTdList.end(), compareLess );
  259. int curr_layer = -1;
  260. for( ZONE* td: m_createdTdList )
  261. {
  262. if( td->GetFirstLayer() != curr_layer )
  263. {
  264. curr_layer = td->GetFirstLayer();
  265. priority_base = MAGIC_TEARDROP_ZONE_ID;
  266. }
  267. td->SetAssignedPriority( priority_base++ );
  268. }
  269. }
  270. void TEARDROP_MANAGER::AddTeardropsOnTracks( BOARD_COMMIT& aCommit,
  271. const std::set<PCB_TRACK*>* aTracks,
  272. bool aForceFullUpdate )
  273. {
  274. std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
  275. TEARDROP_PARAMETERS params = *m_prmsList->GetParameters( TARGET_TRACK );
  276. // Explore groups (a group is a set of tracks on the same layer and the same net):
  277. for( auto& grp : m_trackLookupList.GetBuffer() )
  278. {
  279. int layer, netcode;
  280. TRACK_BUFFER::GetNetcodeAndLayerFromIndex( grp.first, &layer, &netcode );
  281. std::vector<PCB_TRACK*>* sublist = grp.second;
  282. if( sublist->size() <= 1 ) // We need at least 2 track segments
  283. continue;
  284. // The sort function to sort by increasing track widths
  285. struct
  286. {
  287. bool operator()(PCB_TRACK* a, PCB_TRACK* b) const
  288. { return a->GetWidth() < b->GetWidth(); }
  289. } compareLess;
  290. std::sort( sublist->begin(), sublist->end(), compareLess );
  291. int min_width = sublist->front()->GetWidth();
  292. int max_width = sublist->back()->GetWidth();
  293. // Skip groups having the same track thickness
  294. if( max_width == min_width )
  295. continue;
  296. for( unsigned ii = 0; ii < sublist->size()-1; ii++ )
  297. {
  298. PCB_TRACK* track = (*sublist)[ii];
  299. int track_len = (int) track->GetLength();
  300. bool track_needs_update = aForceFullUpdate || alg::contains( *aTracks, track );
  301. min_width = track->GetWidth();
  302. // to avoid creating a teardrop between 2 tracks having similar widths give a threshold
  303. params.m_WidthtoSizeFilterRatio = std::max( params.m_WidthtoSizeFilterRatio, 0.1 );
  304. const double th = 1.0 / params.m_WidthtoSizeFilterRatio;
  305. min_width = KiROUND( min_width * th );
  306. for( unsigned jj = ii+1; jj < sublist->size(); jj++ )
  307. {
  308. // Search candidates with thickness > curr thickness
  309. PCB_TRACK* candidate = (*sublist)[jj];
  310. if( min_width >= candidate->GetWidth() )
  311. continue;
  312. // Cannot build a teardrop on a too short track segment.
  313. // The min len is > candidate radius
  314. if( track_len <= candidate->GetWidth() /2 )
  315. continue;
  316. // Now test end to end connection:
  317. EDA_ITEM_FLAGS match_points; // to return the end point EDA_ITEM_FLAGS:
  318. // 0, STARTPOINT, ENDPOINT
  319. VECTOR2I pos = candidate->GetStart();
  320. match_points = track->IsPointOnEnds( pos, m_tolerance );
  321. if( !match_points )
  322. {
  323. pos = candidate->GetEnd();
  324. match_points = track->IsPointOnEnds( pos, m_tolerance );
  325. }
  326. if( !match_points )
  327. continue;
  328. if( !track_needs_update && alg::contains( *aTracks, candidate ) )
  329. continue;
  330. // Pads/vias have priority for teardrops; ensure there isn't one at our position
  331. bool existingPadOrVia = false;
  332. std::vector<PAD*> connectedPads;
  333. std::vector<PCB_VIA*> connectedVias;
  334. connectivity->GetConnectedPadsAndVias( track, &connectedPads, &connectedVias );
  335. for( PAD* pad : connectedPads )
  336. {
  337. if( pad->HitTest( pos ) )
  338. existingPadOrVia = true;
  339. }
  340. for( PCB_VIA* via : connectedVias )
  341. {
  342. if( via->HitTest( pos ) )
  343. existingPadOrVia = true;
  344. }
  345. if( existingPadOrVia )
  346. continue;
  347. std::vector<VECTOR2I> points;
  348. if( computeTeardropPolygon( params, points, track, candidate, pos ) )
  349. {
  350. ZONE* new_teardrop = createTeardrop( TD_TYPE_TRACKEND, points, track );
  351. m_board->Add( new_teardrop, ADD_MODE::BULK_INSERT );
  352. m_createdTdList.push_back( new_teardrop );
  353. aCommit.Added( new_teardrop );
  354. }
  355. }
  356. }
  357. }
  358. }