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.

496 lines
18 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 The 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->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::NEVER );
  64. teardrop->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::INVISIBLE_BORDER, 0, false );
  65. SHAPE_POLY_SET* outline = teardrop->Outline();
  66. outline->NewOutline();
  67. for( const VECTOR2I& pt: aPoints )
  68. outline->Append( pt.x, pt.y );
  69. // Until we know better (ie: pay for a potentially very expensive zone refill), the teardrop
  70. // fill is the same as its outline.
  71. teardrop->SetFilledPolysList( aTrack->GetLayer(), *teardrop->Outline() );
  72. teardrop->SetIsFilled( true );
  73. // Used in priority calculations:
  74. teardrop->CalculateFilledArea();
  75. return teardrop;
  76. }
  77. ZONE* TEARDROP_MANAGER::createTeardropMask( TEARDROP_VARIANT aTeardropVariant,
  78. std::vector<VECTOR2I>& aPoints, PCB_TRACK* aTrack ) const
  79. {
  80. ZONE* teardrop = new ZONE( m_board );
  81. teardrop->SetTeardropAreaType( aTeardropVariant == TD_TYPE_PADVIA ? TEARDROP_TYPE::TD_VIAPAD
  82. : TEARDROP_TYPE::TD_TRACKEND );
  83. teardrop->SetLayer( aTrack->GetLayer() == F_Cu ? F_Mask : B_Mask );
  84. teardrop->SetMinThickness( pcbIUScale.mmToIU( 0.0254 ) ); // The minimum zone thickness
  85. teardrop->SetIsFilled( false );
  86. teardrop->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::NEVER );
  87. teardrop->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::INVISIBLE_BORDER, 0, false );
  88. SHAPE_POLY_SET* outline = teardrop->Outline();
  89. outline->NewOutline();
  90. for( const VECTOR2I& pt: aPoints )
  91. outline->Append( pt.x, pt.y );
  92. if( int expansion = aTrack->GetSolderMaskExpansion() )
  93. {
  94. // The zone-min-thickness deflate/reinflate is going to round corners, so it's more
  95. // efficient to allow acute corners on the solder mask expansion here, and delegate the
  96. // rounding to the deflate/reinflate.
  97. teardrop->SetMinThickness( std::max( teardrop->GetMinThickness(), expansion ) );
  98. outline->Inflate( expansion, CORNER_STRATEGY::ALLOW_ACUTE_CORNERS,
  99. m_board->GetDesignSettings().m_MaxError );
  100. }
  101. // Until we know better (ie: pay for a potentially very expensive zone refill), the teardrop
  102. // fill is the same as its outline.
  103. teardrop->SetFilledPolysList( teardrop->GetLayer(), *teardrop->Outline() );
  104. teardrop->SetIsFilled( true );
  105. return teardrop;
  106. }
  107. void TEARDROP_MANAGER::RemoveTeardrops( BOARD_COMMIT& aCommit,
  108. const std::vector<BOARD_ITEM*>* dirtyPadsAndVias,
  109. const std::set<PCB_TRACK*>* dirtyTracks )
  110. {
  111. std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
  112. auto isStale =
  113. [&]( ZONE* zone )
  114. {
  115. std::vector<PAD*> connectedPads;
  116. std::vector<PCB_VIA*> connectedVias;
  117. connectivity->GetConnectedPadsAndVias( zone, &connectedPads, &connectedVias );
  118. for( PAD* pad : connectedPads )
  119. {
  120. if( alg::contains( *dirtyPadsAndVias, pad ) )
  121. return true;
  122. }
  123. for( PCB_VIA* via : connectedVias )
  124. {
  125. if( alg::contains( *dirtyPadsAndVias, via ) )
  126. return true;
  127. }
  128. for( PCB_TRACK* track : connectivity->GetConnectedTracks( zone ) )
  129. {
  130. if( alg::contains( *dirtyTracks, track ) )
  131. return true;
  132. }
  133. return false;
  134. };
  135. for( ZONE* zone : m_board->Zones() )
  136. {
  137. if( zone->IsTeardropArea() && isStale( zone ) )
  138. zone->SetFlags( STRUCT_DELETED );
  139. }
  140. m_board->BulkRemoveStaleTeardrops( aCommit );
  141. }
  142. void TEARDROP_MANAGER::UpdateTeardrops( BOARD_COMMIT& aCommit,
  143. const std::vector<BOARD_ITEM*>* dirtyPadsAndVias,
  144. const std::set<PCB_TRACK*>* dirtyTracks,
  145. bool aForceFullUpdate )
  146. {
  147. if( m_board->LegacyTeardrops() )
  148. return;
  149. // Init parameters:
  150. m_tolerance = pcbIUScale.mmToIU( 0.01 );
  151. buildTrackCaches();
  152. // Old teardrops must be removed, to ensure a clean teardrop rebuild
  153. if( aForceFullUpdate )
  154. {
  155. for( ZONE* zone : m_board->Zones() )
  156. {
  157. if( zone->IsTeardropArea() )
  158. zone->SetFlags( STRUCT_DELETED );
  159. }
  160. m_board->BulkRemoveStaleTeardrops( aCommit );
  161. }
  162. std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
  163. for( PCB_TRACK* track : m_board->Tracks() )
  164. {
  165. if( ! ( track->Type() == PCB_TRACE_T || track->Type() == PCB_ARC_T ) )
  166. continue;
  167. std::vector<PAD*> connectedPads;
  168. std::vector<PCB_VIA*> connectedVias;
  169. connectivity->GetConnectedPadsAndVias( track, &connectedPads, &connectedVias );
  170. bool forceUpdate = aForceFullUpdate || dirtyTracks->contains( track );
  171. for( PAD* pad : connectedPads )
  172. {
  173. if( !forceUpdate && !alg::contains( *dirtyPadsAndVias, pad ) )
  174. continue;
  175. TEARDROP_PARAMETERS& tdParams = pad->GetTeardropParams();
  176. VECTOR2I padSize = pad->GetSize( track->GetLayer() );
  177. int annularWidth = std::min( padSize.x, padSize.y );
  178. if( !tdParams.m_Enabled )
  179. continue;
  180. // Ensure a teardrop shape can be built: track width must be < teardrop width and
  181. // filter width
  182. if( track->GetWidth() >= tdParams.m_TdMaxWidth
  183. || track->GetWidth() >= annularWidth * tdParams.m_BestWidthRatio
  184. || track->GetWidth() >= annularWidth * tdParams.m_WidthtoSizeFilterRatio )
  185. {
  186. continue;
  187. }
  188. if( pad->HitTest( track->GetStart() ) && pad->HitTest( track->GetEnd() ) )
  189. // The track is entirely inside the pad; cannot create a teardrop
  190. continue;
  191. // Skip case where pad and the track are within a copper zone with the same net
  192. // (and the pad can be connected to the zone)
  193. if( !tdParams.m_TdOnPadsInZones && areItemsInSameZone( pad, track ) )
  194. continue;
  195. std::vector<VECTOR2I> points;
  196. if( computeTeardropPolygon( tdParams, points, track, pad, pad->GetPosition() ) )
  197. {
  198. ZONE* new_teardrop = createTeardrop( TD_TYPE_PADVIA, points, track );
  199. m_board->Add( new_teardrop, ADD_MODE::BULK_INSERT );
  200. m_createdTdList.push_back( new_teardrop );
  201. aCommit.Added( new_teardrop );
  202. if( track->HasSolderMask() && ( track->GetLayer() == F_Cu || track->GetLayer() == B_Cu ) )
  203. {
  204. ZONE* new_teardrop_mask = createTeardropMask( TD_TYPE_PADVIA, points, track );
  205. m_board->Add( new_teardrop_mask, ADD_MODE::BULK_INSERT );
  206. aCommit.Added( new_teardrop_mask );
  207. }
  208. }
  209. }
  210. for( PCB_VIA* via : connectedVias )
  211. {
  212. if( !forceUpdate && !alg::contains( *dirtyPadsAndVias, via ) )
  213. continue;
  214. TEARDROP_PARAMETERS tdParams = via->GetTeardropParams();
  215. int annularWidth = via->GetWidth( track->GetLayer() );
  216. if( !tdParams.m_Enabled )
  217. continue;
  218. // Ensure a teardrop shape can be built: track width must be < teardrop width and
  219. // filter width
  220. if( track->GetWidth() >= tdParams.m_TdMaxWidth
  221. || track->GetWidth() >= annularWidth * tdParams.m_BestWidthRatio
  222. || track->GetWidth() >= annularWidth * tdParams.m_WidthtoSizeFilterRatio )
  223. {
  224. continue;
  225. }
  226. if( via->HitTest( track->GetStart() ) && via->HitTest( track->GetEnd() ) )
  227. // The track is entirely inside the via; cannot create a teardrop
  228. continue;
  229. std::vector<VECTOR2I> points;
  230. if( computeTeardropPolygon( tdParams, points, track, via, via->GetPosition() ) )
  231. {
  232. ZONE* new_teardrop = createTeardrop( TD_TYPE_PADVIA, points, track );
  233. m_board->Add( new_teardrop, ADD_MODE::BULK_INSERT );
  234. m_createdTdList.push_back( new_teardrop );
  235. aCommit.Added( new_teardrop );
  236. if( track->HasSolderMask() && ( track->GetLayer() == F_Cu || track->GetLayer() == B_Cu ) )
  237. {
  238. ZONE* new_teardrop_mask = createTeardropMask( TD_TYPE_PADVIA, points, track );
  239. m_board->Add( new_teardrop_mask, ADD_MODE::BULK_INSERT );
  240. aCommit.Added( new_teardrop_mask );
  241. }
  242. }
  243. }
  244. }
  245. if( ( aForceFullUpdate || !dirtyTracks->empty() )
  246. && m_prmsList->GetParameters( TARGET_TRACK )->m_Enabled )
  247. {
  248. AddTeardropsOnTracks( aCommit, dirtyTracks, aForceFullUpdate );
  249. }
  250. // Now set priority of teardrops now all teardrops are added
  251. setTeardropPriorities();
  252. }
  253. void TEARDROP_MANAGER::DeleteTrackToTrackTeardrops( BOARD_COMMIT& aCommit )
  254. {
  255. for( ZONE* zone : m_board->Zones() )
  256. {
  257. if( zone->IsTeardropArea() && zone->GetTeardropAreaType() == TEARDROP_TYPE::TD_TRACKEND )
  258. zone->SetFlags( STRUCT_DELETED );
  259. }
  260. m_board->BulkRemoveStaleTeardrops( aCommit );
  261. }
  262. void TEARDROP_MANAGER::setTeardropPriorities()
  263. {
  264. // Note: a teardrop area is on only one layer, so using GetFirstLayer() is OK
  265. // to know the zone layer of a teardrop
  266. int priority_base = MAGIC_TEARDROP_ZONE_ID;
  267. // The sort function to sort by increasing copper layers. Group by layers.
  268. // For same layers sort by decreasing areas
  269. struct
  270. {
  271. bool operator()(ZONE* a, ZONE* b) const
  272. {
  273. if( a->GetFirstLayer() == b->GetFirstLayer() )
  274. return a->GetOutlineArea() > b->GetOutlineArea();
  275. return a->GetFirstLayer() < b->GetFirstLayer();
  276. }
  277. } compareLess;
  278. for( ZONE* td: m_createdTdList )
  279. td->CalculateOutlineArea();
  280. std::sort( m_createdTdList.begin(), m_createdTdList.end(), compareLess );
  281. int curr_layer = -1;
  282. for( ZONE* td: m_createdTdList )
  283. {
  284. if( td->GetFirstLayer() != curr_layer )
  285. {
  286. curr_layer = td->GetFirstLayer();
  287. priority_base = MAGIC_TEARDROP_ZONE_ID;
  288. }
  289. td->SetAssignedPriority( priority_base++ );
  290. }
  291. }
  292. void TEARDROP_MANAGER::AddTeardropsOnTracks( BOARD_COMMIT& aCommit,
  293. const std::set<PCB_TRACK*>* aTracks,
  294. bool aForceFullUpdate )
  295. {
  296. std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
  297. TEARDROP_PARAMETERS params = *m_prmsList->GetParameters( TARGET_TRACK );
  298. // Explore groups (a group is a set of tracks on the same layer and the same net):
  299. for( auto& grp : m_trackLookupList.GetBuffer() )
  300. {
  301. int layer, netcode;
  302. TRACK_BUFFER::GetNetcodeAndLayerFromIndex( grp.first, &layer, &netcode );
  303. std::vector<PCB_TRACK*>* sublist = grp.second;
  304. if( sublist->size() <= 1 ) // We need at least 2 track segments
  305. continue;
  306. // The sort function to sort by increasing track widths
  307. struct
  308. {
  309. bool operator()(PCB_TRACK* a, PCB_TRACK* b) const
  310. { return a->GetWidth() < b->GetWidth(); }
  311. } compareLess;
  312. std::sort( sublist->begin(), sublist->end(), compareLess );
  313. int min_width = sublist->front()->GetWidth();
  314. int max_width = sublist->back()->GetWidth();
  315. // Skip groups having the same track thickness
  316. if( max_width == min_width )
  317. continue;
  318. for( unsigned ii = 0; ii < sublist->size()-1; ii++ )
  319. {
  320. PCB_TRACK* track = (*sublist)[ii];
  321. int track_len = (int) track->GetLength();
  322. bool track_needs_update = aForceFullUpdate || alg::contains( *aTracks, track );
  323. min_width = track->GetWidth();
  324. // to avoid creating a teardrop between 2 tracks having similar widths give a threshold
  325. params.m_WidthtoSizeFilterRatio = std::max( params.m_WidthtoSizeFilterRatio, 0.1 );
  326. const double th = 1.0 / params.m_WidthtoSizeFilterRatio;
  327. min_width = KiROUND( min_width * th );
  328. for( unsigned jj = ii+1; jj < sublist->size(); jj++ )
  329. {
  330. // Search candidates with thickness > curr thickness
  331. PCB_TRACK* candidate = (*sublist)[jj];
  332. if( min_width >= candidate->GetWidth() )
  333. continue;
  334. // Cannot build a teardrop on a too short track segment.
  335. // The min len is > candidate radius
  336. if( track_len <= candidate->GetWidth() /2 )
  337. continue;
  338. // Now test end to end connection:
  339. EDA_ITEM_FLAGS match_points; // to return the end point EDA_ITEM_FLAGS:
  340. // 0, STARTPOINT, ENDPOINT
  341. VECTOR2I pos = candidate->GetStart();
  342. match_points = track->IsPointOnEnds( pos, m_tolerance );
  343. if( !match_points )
  344. {
  345. pos = candidate->GetEnd();
  346. match_points = track->IsPointOnEnds( pos, m_tolerance );
  347. }
  348. if( !match_points )
  349. continue;
  350. if( !track_needs_update && alg::contains( *aTracks, candidate ) )
  351. continue;
  352. // Pads/vias have priority for teardrops; ensure there isn't one at our position
  353. bool existingPadOrVia = false;
  354. std::vector<PAD*> connectedPads;
  355. std::vector<PCB_VIA*> connectedVias;
  356. connectivity->GetConnectedPadsAndVias( track, &connectedPads, &connectedVias );
  357. for( PAD* pad : connectedPads )
  358. {
  359. if( pad->HitTest( pos ) )
  360. existingPadOrVia = true;
  361. }
  362. for( PCB_VIA* via : connectedVias )
  363. {
  364. if( via->HitTest( pos ) )
  365. existingPadOrVia = true;
  366. }
  367. if( existingPadOrVia )
  368. continue;
  369. std::vector<VECTOR2I> points;
  370. if( computeTeardropPolygon( params, points, track, candidate, pos ) )
  371. {
  372. ZONE* new_teardrop = createTeardrop( TD_TYPE_TRACKEND, points, track );
  373. m_board->Add( new_teardrop, ADD_MODE::BULK_INSERT );
  374. m_createdTdList.push_back( new_teardrop );
  375. aCommit.Added( new_teardrop );
  376. if( track->HasSolderMask() && ( track->GetLayer() == F_Cu || track->GetLayer() == B_Cu ) )
  377. {
  378. ZONE* new_teardrop_mask = createTeardropMask( TD_TYPE_TRACKEND, points, track );
  379. m_board->Add( new_teardrop_mask, ADD_MODE::BULK_INSERT );
  380. aCommit.Added( new_teardrop_mask );
  381. }
  382. }
  383. }
  384. }
  385. }
  386. }