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.

914 lines
30 KiB

11 years ago
9 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
9 years ago
11 years ago
5 years ago
11 years ago
11 years ago
4 years ago
4 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
11 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2014 CERN
  5. * Copyright (C) 2018-2022 KiCad Developers, see AUTHORS.txt for contributors.
  6. * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  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 <functional>
  26. #include <pcb_dimension.h>
  27. #include <fp_shape.h>
  28. #include <footprint.h>
  29. #include <pad.h>
  30. #include <pcb_group.h>
  31. #include <pcb_track.h>
  32. #include <zone.h>
  33. #include <geometry/shape_circle.h>
  34. #include <geometry/shape_line_chain.h>
  35. #include <geometry/shape_rect.h>
  36. #include <geometry/shape_segment.h>
  37. #include <geometry/shape_simple.h>
  38. #include <macros.h>
  39. #include <math/util.h> // for KiROUND
  40. #include <painter.h>
  41. #include <pcbnew_settings.h>
  42. #include <tool/tool_manager.h>
  43. #include <tools/pcb_tool_base.h>
  44. #include <view/view.h>
  45. #include "pcb_grid_helper.h"
  46. PCB_GRID_HELPER::PCB_GRID_HELPER( TOOL_MANAGER* aToolMgr, MAGNETIC_SETTINGS* aMagneticSettings ) :
  47. GRID_HELPER( aToolMgr ),
  48. m_magneticSettings( aMagneticSettings )
  49. {
  50. KIGFX::VIEW* view = m_toolMgr->GetView();
  51. KIGFX::RENDER_SETTINGS* settings = view->GetPainter()->GetSettings();
  52. KIGFX::COLOR4D auxItemsColor = settings->GetLayerColor( LAYER_AUX_ITEMS );
  53. KIGFX::COLOR4D umbilicalColor = settings->GetLayerColor( LAYER_ANCHOR );
  54. m_viewAxis.SetSize( 20000 );
  55. m_viewAxis.SetStyle( KIGFX::ORIGIN_VIEWITEM::CROSS );
  56. m_viewAxis.SetColor( auxItemsColor.WithAlpha( 0.4 ) );
  57. m_viewAxis.SetDrawAtZero( true );
  58. view->Add( &m_viewAxis );
  59. view->SetVisible( &m_viewAxis, false );
  60. m_viewSnapPoint.SetStyle( KIGFX::ORIGIN_VIEWITEM::CIRCLE_CROSS );
  61. m_viewSnapPoint.SetColor( auxItemsColor );
  62. m_viewSnapPoint.SetDrawAtZero( true );
  63. view->Add( &m_viewSnapPoint );
  64. view->SetVisible( &m_viewSnapPoint, false );
  65. m_viewSnapLine.SetStyle( KIGFX::ORIGIN_VIEWITEM::DASH_LINE );
  66. m_viewSnapLine.SetColor( umbilicalColor );
  67. m_viewSnapLine.SetDrawAtZero( true );
  68. view->Add( &m_viewSnapLine );
  69. view->SetVisible( &m_viewSnapLine, false );
  70. }
  71. VECTOR2I PCB_GRID_HELPER::AlignToSegment( const VECTOR2I& aPoint, const SEG& aSeg )
  72. {
  73. const int c_gridSnapEpsilon_sq = 4;
  74. VECTOR2I aligned = Align( aPoint );
  75. if( !m_enableSnap )
  76. return aligned;
  77. std::vector<VECTOR2I> points;
  78. const SEG testSegments[] = { SEG( aligned, aligned + VECTOR2( 1, 0 ) ),
  79. SEG( aligned, aligned + VECTOR2( 0, 1 ) ),
  80. SEG( aligned, aligned + VECTOR2( 1, 1 ) ),
  81. SEG( aligned, aligned + VECTOR2( 1, -1 ) ) };
  82. for( const SEG& seg : testSegments )
  83. {
  84. OPT_VECTOR2I vec = aSeg.IntersectLines( seg );
  85. if( vec && aSeg.SquaredDistance( *vec ) <= c_gridSnapEpsilon_sq )
  86. points.push_back( *vec );
  87. }
  88. VECTOR2I nearest = aligned;
  89. SEG::ecoord min_d_sq = VECTOR2I::ECOORD_MAX;
  90. // Snap by distance between pointer and endpoints
  91. for( const VECTOR2I& pt : { aSeg.A, aSeg.B } )
  92. {
  93. SEG::ecoord d_sq = ( pt - aPoint ).SquaredEuclideanNorm();
  94. if( d_sq < min_d_sq )
  95. {
  96. min_d_sq = d_sq;
  97. nearest = pt;
  98. }
  99. }
  100. // Snap by distance between aligned cursor and intersections
  101. for( const VECTOR2I& pt : points )
  102. {
  103. SEG::ecoord d_sq = ( pt - aligned ).SquaredEuclideanNorm();
  104. if( d_sq < min_d_sq )
  105. {
  106. min_d_sq = d_sq;
  107. nearest = pt;
  108. }
  109. }
  110. return nearest;
  111. }
  112. VECTOR2I PCB_GRID_HELPER::AlignToArc( const VECTOR2I& aPoint, const SHAPE_ARC& aArc )
  113. {
  114. VECTOR2I aligned = Align( aPoint );
  115. if( !m_enableSnap )
  116. return aligned;
  117. std::vector<VECTOR2I> points;
  118. aArc.IntersectLine( SEG( aligned, aligned + VECTOR2( 1, 0 ) ), &points );
  119. aArc.IntersectLine( SEG( aligned, aligned + VECTOR2( 0, 1 ) ), &points );
  120. aArc.IntersectLine( SEG( aligned, aligned + VECTOR2( 1, 1 ) ), &points );
  121. aArc.IntersectLine( SEG( aligned, aligned + VECTOR2( 1, -1 ) ), &points );
  122. VECTOR2I nearest = aligned;
  123. SEG::ecoord min_d_sq = VECTOR2I::ECOORD_MAX;
  124. // Snap by distance between pointer and endpoints
  125. for( const VECTOR2I& pt : { aArc.GetP0(), aArc.GetP1() } )
  126. {
  127. SEG::ecoord d_sq = ( pt - aPoint ).SquaredEuclideanNorm();
  128. if( d_sq < min_d_sq )
  129. {
  130. min_d_sq = d_sq;
  131. nearest = pt;
  132. }
  133. }
  134. // Snap by distance between aligned cursor and intersections
  135. for( const VECTOR2I& pt : points )
  136. {
  137. SEG::ecoord d_sq = ( pt - aligned ).SquaredEuclideanNorm();
  138. if( d_sq < min_d_sq )
  139. {
  140. min_d_sq = d_sq;
  141. nearest = pt;
  142. }
  143. }
  144. return nearest;
  145. }
  146. VECTOR2I PCB_GRID_HELPER::AlignToNearestPad( const VECTOR2I& aMousePos, PADS& aPads )
  147. {
  148. clearAnchors();
  149. for( BOARD_ITEM* item : aPads )
  150. computeAnchors( item, aMousePos, true );
  151. double minDist = std::numeric_limits<double>::max();
  152. ANCHOR* nearestOrigin = nullptr;
  153. for( ANCHOR& a : m_anchors )
  154. {
  155. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( a.item );
  156. if( ( ORIGIN & a.flags ) != ORIGIN )
  157. continue;
  158. if( !item->HitTest( aMousePos ) )
  159. continue;
  160. double dist = a.Distance( aMousePos );
  161. if( dist < minDist )
  162. {
  163. minDist = dist;
  164. nearestOrigin = &a;
  165. }
  166. }
  167. return nearestOrigin ? nearestOrigin->pos : aMousePos;
  168. }
  169. VECTOR2I PCB_GRID_HELPER::BestDragOrigin( const VECTOR2I &aMousePos,
  170. std::vector<BOARD_ITEM*>& aItems,
  171. const SELECTION_FILTER_OPTIONS* aSelectionFilter )
  172. {
  173. clearAnchors();
  174. for( BOARD_ITEM* item : aItems )
  175. computeAnchors( item, aMousePos, true, aSelectionFilter );
  176. double worldScale = m_toolMgr->GetView()->GetGAL()->GetWorldScale();
  177. double lineSnapMinCornerDistance = 50.0 / worldScale;
  178. ANCHOR* nearestOutline = nearestAnchor( aMousePos, OUTLINE, LSET::AllLayersMask() );
  179. ANCHOR* nearestCorner = nearestAnchor( aMousePos, CORNER, LSET::AllLayersMask() );
  180. ANCHOR* nearestOrigin = nearestAnchor( aMousePos, ORIGIN, LSET::AllLayersMask() );
  181. ANCHOR* best = nullptr;
  182. double minDist = std::numeric_limits<double>::max();
  183. if( nearestOrigin )
  184. {
  185. minDist = nearestOrigin->Distance( aMousePos );
  186. best = nearestOrigin;
  187. }
  188. if( nearestCorner )
  189. {
  190. double dist = nearestCorner->Distance( aMousePos );
  191. if( dist < minDist )
  192. {
  193. minDist = dist;
  194. best = nearestCorner;
  195. }
  196. }
  197. if( nearestOutline )
  198. {
  199. double dist = nearestOutline->Distance( aMousePos );
  200. if( minDist > lineSnapMinCornerDistance && dist < minDist )
  201. best = nearestOutline;
  202. }
  203. return best ? best->pos : aMousePos;
  204. }
  205. VECTOR2I PCB_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, BOARD_ITEM* aReferenceItem )
  206. {
  207. LSET layers;
  208. std::vector<BOARD_ITEM*> item;
  209. if( aReferenceItem )
  210. {
  211. layers = aReferenceItem->GetLayerSet();
  212. item.push_back( aReferenceItem );
  213. }
  214. else
  215. {
  216. layers = LSET::AllLayersMask();
  217. }
  218. return BestSnapAnchor( aOrigin, layers, item );
  219. }
  220. VECTOR2I PCB_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& aLayers,
  221. const std::vector<BOARD_ITEM*>& aSkip )
  222. {
  223. // Tuning constant: snap radius in screen space
  224. const int snapSize = 25;
  225. // Snapping distance is in screen space, clamped to the current grid to ensure that the grid
  226. // points that are visible can always be snapped to.
  227. // see https://gitlab.com/kicad/code/kicad/-/issues/5638
  228. // see https://gitlab.com/kicad/code/kicad/-/issues/7125
  229. double snapScale = snapSize / m_toolMgr->GetView()->GetGAL()->GetWorldScale();
  230. int snapRange = std::min( KiROUND( snapScale ), GetGrid().x );
  231. int snapDist = snapRange;
  232. //Respect limits of coordinates representation
  233. BOX2I bb;
  234. bb.SetOrigin( GetClampedCoords<double, int>( VECTOR2D( aOrigin ) - snapRange / 2.0 ) );
  235. bb.SetEnd( GetClampedCoords<double, int>( VECTOR2D( aOrigin ) + snapRange / 2.0 ) );
  236. clearAnchors();
  237. for( BOARD_ITEM* item : queryVisible( bb, aSkip ) )
  238. computeAnchors( item, aOrigin );
  239. ANCHOR* nearest = nearestAnchor( aOrigin, SNAPPABLE, aLayers );
  240. VECTOR2I nearestGrid = Align( aOrigin );
  241. if( nearest )
  242. snapDist = nearest->Distance( aOrigin );
  243. // Existing snap lines need priority over new snaps
  244. if( m_snapItem && m_enableSnapLine && m_enableSnap )
  245. {
  246. bool snapLine = false;
  247. int x_dist = std::abs( m_viewSnapLine.GetPosition().x - aOrigin.x );
  248. int y_dist = std::abs( m_viewSnapLine.GetPosition().y - aOrigin.y );
  249. /// Allows de-snapping from the line if you are closer to another snap point
  250. if( x_dist < snapRange && ( !nearest || snapDist > snapRange ) )
  251. {
  252. nearestGrid.x = m_viewSnapLine.GetPosition().x;
  253. snapLine = true;
  254. }
  255. if( y_dist < snapRange && ( !nearest || snapDist > snapRange ) )
  256. {
  257. nearestGrid.y = m_viewSnapLine.GetPosition().y;
  258. snapLine = true;
  259. }
  260. if( snapLine && m_skipPoint != VECTOR2I( m_viewSnapLine.GetPosition() ) )
  261. {
  262. m_viewSnapLine.SetEndPosition( nearestGrid );
  263. if( m_toolMgr->GetView()->IsVisible( &m_viewSnapLine ) )
  264. m_toolMgr->GetView()->Update( &m_viewSnapLine, KIGFX::GEOMETRY );
  265. else
  266. m_toolMgr->GetView()->SetVisible( &m_viewSnapLine, true );
  267. return nearestGrid;
  268. }
  269. }
  270. if( nearest && m_enableSnap )
  271. {
  272. if( nearest->Distance( aOrigin ) <= snapRange )
  273. {
  274. m_viewSnapPoint.SetPosition( wxPoint( nearest->pos ) );
  275. m_viewSnapLine.SetPosition( wxPoint( nearest->pos ) );
  276. m_toolMgr->GetView()->SetVisible( &m_viewSnapLine, false );
  277. if( m_toolMgr->GetView()->IsVisible( &m_viewSnapPoint ) )
  278. m_toolMgr->GetView()->Update( &m_viewSnapPoint, KIGFX::GEOMETRY);
  279. else
  280. m_toolMgr->GetView()->SetVisible( &m_viewSnapPoint, true );
  281. m_snapItem = nearest;
  282. return nearest->pos;
  283. }
  284. }
  285. m_snapItem = nullptr;
  286. m_toolMgr->GetView()->SetVisible( &m_viewSnapPoint, false );
  287. m_toolMgr->GetView()->SetVisible( &m_viewSnapLine, false );
  288. return nearestGrid;
  289. }
  290. BOARD_ITEM* PCB_GRID_HELPER::GetSnapped() const
  291. {
  292. if( !m_snapItem )
  293. return nullptr;
  294. return static_cast<BOARD_ITEM*>( m_snapItem->item );
  295. }
  296. std::set<BOARD_ITEM*> PCB_GRID_HELPER::queryVisible( const BOX2I& aArea,
  297. const std::vector<BOARD_ITEM*>& aSkip ) const
  298. {
  299. std::set<BOARD_ITEM*> items;
  300. std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
  301. KIGFX::VIEW* view = m_toolMgr->GetView();
  302. RENDER_SETTINGS* settings = view->GetPainter()->GetSettings();
  303. const std::set<int>& activeLayers = settings->GetHighContrastLayers();
  304. bool isHighContrast = settings->GetHighContrast();
  305. view->Query( aArea, selectedItems );
  306. for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : selectedItems )
  307. {
  308. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
  309. // If we are in the footprint editor, don't use the footprint itself
  310. if( static_cast<PCB_TOOL_BASE*>( m_toolMgr->GetCurrentTool() )->IsFootprintEditor()
  311. && item->Type() == PCB_FOOTPRINT_T )
  312. {
  313. continue;
  314. }
  315. // The item must be visible and on an active layer
  316. if( view->IsVisible( item )
  317. && ( !isHighContrast || activeLayers.count( it.second ) )
  318. && item->ViewGetLOD( it.second, view ) < view->GetScale() )
  319. {
  320. items.insert ( item );
  321. }
  322. }
  323. for( BOARD_ITEM* skipItem : aSkip )
  324. items.erase( skipItem );
  325. return items;
  326. }
  327. void PCB_GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos, bool aFrom,
  328. const SELECTION_FILTER_OPTIONS* aSelectionFilter )
  329. {
  330. KIGFX::VIEW* view = m_toolMgr->GetView();
  331. RENDER_SETTINGS* settings = view->GetPainter()->GetSettings();
  332. const std::set<int>& activeLayers = settings->GetHighContrastLayers();
  333. bool isHighContrast = settings->GetHighContrast();
  334. auto handlePadShape =
  335. [&]( PAD* aPad )
  336. {
  337. addAnchor( aPad->GetPosition(), ORIGIN | SNAPPABLE, aPad );
  338. /// If we are getting a drag point, we don't want to center the edge of pads
  339. if( aFrom )
  340. return;
  341. switch( aPad->GetShape() )
  342. {
  343. case PAD_SHAPE::CIRCLE:
  344. {
  345. int r = aPad->GetSizeX() / 2;
  346. VECTOR2I center = aPad->ShapePos();
  347. addAnchor( center + VECTOR2I( -r, 0 ), OUTLINE | SNAPPABLE, aPad );
  348. addAnchor( center + VECTOR2I( r, 0 ), OUTLINE | SNAPPABLE, aPad );
  349. addAnchor( center + VECTOR2I( 0, -r ), OUTLINE | SNAPPABLE, aPad );
  350. addAnchor( center + VECTOR2I( 0, r ), OUTLINE | SNAPPABLE, aPad );
  351. break;
  352. }
  353. case PAD_SHAPE::OVAL:
  354. {
  355. VECTOR2I pos = aPad->ShapePos();
  356. VECTOR2I half_size = aPad->GetSize() / 2;
  357. int half_width = std::min( half_size.x, half_size.y );
  358. VECTOR2I half_len( half_size.x - half_width, half_size.y - half_width );
  359. RotatePoint( half_len, aPad->GetOrientation() );
  360. VECTOR2I a( pos - half_len );
  361. VECTOR2I b( pos + half_len );
  362. VECTOR2I normal = b - a;
  363. normal.Resize( half_width );
  364. RotatePoint( normal, ANGLE_90 );
  365. addAnchor( a + normal, OUTLINE | SNAPPABLE, aPad );
  366. addAnchor( a - normal, OUTLINE | SNAPPABLE, aPad );
  367. addAnchor( b + normal, OUTLINE | SNAPPABLE, aPad );
  368. addAnchor( b - normal, OUTLINE | SNAPPABLE, aPad );
  369. addAnchor( pos + normal, OUTLINE | SNAPPABLE, aPad );
  370. addAnchor( pos - normal, OUTLINE | SNAPPABLE, aPad );
  371. RotatePoint( normal, -ANGLE_90 );
  372. addAnchor( a - normal, OUTLINE | SNAPPABLE, aPad );
  373. addAnchor( b + normal, OUTLINE | SNAPPABLE, aPad );
  374. break;
  375. }
  376. case PAD_SHAPE::RECT:
  377. case PAD_SHAPE::TRAPEZOID:
  378. case PAD_SHAPE::ROUNDRECT:
  379. case PAD_SHAPE::CHAMFERED_RECT:
  380. {
  381. VECTOR2I half_size( aPad->GetSize() / 2 );
  382. VECTOR2I trap_delta( 0, 0 );
  383. if( aPad->GetShape() == PAD_SHAPE::TRAPEZOID )
  384. trap_delta = aPad->GetDelta() / 2;
  385. SHAPE_LINE_CHAIN corners;
  386. corners.Append( -half_size.x - trap_delta.y, half_size.y + trap_delta.x );
  387. corners.Append( half_size.x + trap_delta.y, half_size.y - trap_delta.x );
  388. corners.Append( half_size.x - trap_delta.y, -half_size.y + trap_delta.x );
  389. corners.Append( -half_size.x + trap_delta.y, -half_size.y - trap_delta.x );
  390. corners.SetClosed( true );
  391. corners.Rotate( aPad->GetOrientation() );
  392. corners.Move( aPad->ShapePos() );
  393. for( size_t ii = 0; ii < corners.GetSegmentCount(); ++ii )
  394. {
  395. const SEG& seg = corners.GetSegment( ii );
  396. addAnchor( seg.A, OUTLINE | SNAPPABLE, aPad );
  397. addAnchor( seg.Center(), OUTLINE | SNAPPABLE, aPad );
  398. if( ii == corners.GetSegmentCount() - 1 )
  399. addAnchor( seg.B, OUTLINE | SNAPPABLE, aPad );
  400. }
  401. break;
  402. }
  403. default:
  404. {
  405. const std::shared_ptr<SHAPE_POLY_SET>& outline = aPad->GetEffectivePolygon();
  406. if( !outline->IsEmpty() )
  407. {
  408. for( const VECTOR2I& pt : outline->Outline( 0 ).CPoints() )
  409. addAnchor( pt, OUTLINE | SNAPPABLE, aPad );
  410. }
  411. break;
  412. }
  413. }
  414. };
  415. auto handleShape =
  416. [&]( PCB_SHAPE* shape )
  417. {
  418. VECTOR2I start = shape->GetStart();
  419. VECTOR2I end = shape->GetEnd();
  420. switch( shape->GetShape() )
  421. {
  422. case SHAPE_T::CIRCLE:
  423. {
  424. int r = ( start - end ).EuclideanNorm();
  425. addAnchor( start, ORIGIN | SNAPPABLE, shape );
  426. addAnchor( start + VECTOR2I( -r, 0 ), OUTLINE | SNAPPABLE, shape );
  427. addAnchor( start + VECTOR2I( r, 0 ), OUTLINE | SNAPPABLE, shape );
  428. addAnchor( start + VECTOR2I( 0, -r ), OUTLINE | SNAPPABLE, shape );
  429. addAnchor( start + VECTOR2I( 0, r ), OUTLINE | SNAPPABLE, shape );
  430. break;
  431. }
  432. case SHAPE_T::ARC:
  433. addAnchor( shape->GetStart(), CORNER | SNAPPABLE, shape );
  434. addAnchor( shape->GetEnd(), CORNER | SNAPPABLE, shape );
  435. addAnchor( shape->GetArcMid(), CORNER | SNAPPABLE, shape );
  436. addAnchor( shape->GetCenter(), ORIGIN | SNAPPABLE, shape );
  437. break;
  438. case SHAPE_T::RECT:
  439. {
  440. VECTOR2I point2( end.x, start.y );
  441. VECTOR2I point3( start.x, end.y );
  442. SEG first( start, point2 );
  443. SEG second( point2, end );
  444. SEG third( end, point3 );
  445. SEG fourth( point3, start );
  446. addAnchor( first.A, CORNER | SNAPPABLE, shape );
  447. addAnchor( first.Center(), CORNER | SNAPPABLE, shape );
  448. addAnchor( second.A, CORNER | SNAPPABLE, shape );
  449. addAnchor( second.Center(), CORNER | SNAPPABLE, shape );
  450. addAnchor( third.A, CORNER | SNAPPABLE, shape );
  451. addAnchor( third.Center(), CORNER | SNAPPABLE, shape );
  452. addAnchor( fourth.A, CORNER | SNAPPABLE, shape );
  453. addAnchor( fourth.Center(), CORNER | SNAPPABLE, shape );
  454. break;
  455. }
  456. case SHAPE_T::SEGMENT:
  457. addAnchor( start, CORNER | SNAPPABLE, shape );
  458. addAnchor( end, CORNER | SNAPPABLE, shape );
  459. addAnchor( shape->GetCenter(), CORNER | SNAPPABLE, shape );
  460. break;
  461. case SHAPE_T::POLY:
  462. {
  463. SHAPE_LINE_CHAIN lc;
  464. lc.SetClosed( true );
  465. std::vector<VECTOR2I> poly;
  466. shape->DupPolyPointsList( poly );
  467. for( const VECTOR2I& p : poly )
  468. {
  469. addAnchor( p, CORNER | SNAPPABLE, shape );
  470. lc.Append( p );
  471. }
  472. addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem );
  473. break;
  474. }
  475. case SHAPE_T::BEZIER:
  476. addAnchor( start, CORNER | SNAPPABLE, shape );
  477. addAnchor( end, CORNER | SNAPPABLE, shape );
  478. KI_FALLTHROUGH;
  479. default:
  480. addAnchor( shape->GetPosition(), ORIGIN | SNAPPABLE, shape );
  481. break;
  482. }
  483. };
  484. switch( aItem->Type() )
  485. {
  486. case PCB_FOOTPRINT_T:
  487. {
  488. FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem );
  489. for( PAD* pad : footprint->Pads() )
  490. {
  491. if( aFrom )
  492. {
  493. if( aSelectionFilter && !aSelectionFilter->pads )
  494. continue;
  495. }
  496. else
  497. {
  498. if( m_magneticSettings->pads != MAGNETIC_OPTIONS::CAPTURE_ALWAYS )
  499. continue;
  500. }
  501. if( !view->IsVisible( pad ) || !pad->GetBoundingBox().Contains( aRefPos ) )
  502. continue;
  503. // Getting pads from a footprint requires re-checking that the pads are shown
  504. bool onActiveLayer = !isHighContrast;
  505. bool isLODVisible = false;
  506. for( PCB_LAYER_ID layer : pad->GetLayerSet().Seq() )
  507. {
  508. if( !onActiveLayer && activeLayers.count( layer ) )
  509. onActiveLayer = true;
  510. if( !isLODVisible && pad->ViewGetLOD( layer, view ) < view->GetScale() )
  511. isLODVisible = true;
  512. if( onActiveLayer && isLODVisible )
  513. {
  514. handlePadShape( pad );
  515. break;
  516. }
  517. }
  518. }
  519. if( aFrom && aSelectionFilter && !aSelectionFilter->footprints )
  520. break;
  521. // if the cursor is not over a pad, then drag the footprint by its origin
  522. VECTOR2I position = footprint->GetPosition();
  523. addAnchor( position, ORIGIN | SNAPPABLE, footprint );
  524. // Add the footprint center point if it is markedly different from the origin
  525. VECTOR2I center = footprint->GetBoundingBox( false, false ).Centre();
  526. VECTOR2I grid( GetGrid() );
  527. if( ( center - position ).SquaredEuclideanNorm() > grid.SquaredEuclideanNorm() )
  528. addAnchor( center, ORIGIN | SNAPPABLE, footprint );
  529. break;
  530. }
  531. case PCB_PAD_T:
  532. if( aFrom )
  533. {
  534. if( aSelectionFilter && !aSelectionFilter->pads )
  535. break;
  536. }
  537. else
  538. {
  539. if( m_magneticSettings->pads != MAGNETIC_OPTIONS::CAPTURE_ALWAYS )
  540. break;
  541. }
  542. handlePadShape( static_cast<PAD*>( aItem ) );
  543. break;
  544. case PCB_FP_TEXTBOX_T:
  545. case PCB_TEXTBOX_T:
  546. if( aFrom )
  547. {
  548. if( aSelectionFilter && !aSelectionFilter->text )
  549. break;
  550. }
  551. else
  552. {
  553. if( !m_magneticSettings->graphics )
  554. break;
  555. }
  556. handleShape( static_cast<PCB_SHAPE*>( aItem ) );
  557. break;
  558. case PCB_FP_SHAPE_T:
  559. case PCB_SHAPE_T:
  560. if( aFrom )
  561. {
  562. if( aSelectionFilter && !aSelectionFilter->graphics )
  563. break;
  564. }
  565. else
  566. {
  567. if( !m_magneticSettings->graphics )
  568. break;
  569. }
  570. handleShape( static_cast<PCB_SHAPE*>( aItem ) );
  571. break;
  572. case PCB_TRACE_T:
  573. case PCB_ARC_T:
  574. {
  575. if( aFrom )
  576. {
  577. if( aSelectionFilter && !aSelectionFilter->tracks )
  578. break;
  579. }
  580. else
  581. {
  582. if( m_magneticSettings->tracks != MAGNETIC_OPTIONS::CAPTURE_ALWAYS )
  583. break;
  584. }
  585. PCB_TRACK* track = static_cast<PCB_TRACK*>( aItem );
  586. addAnchor( track->GetStart(), CORNER | SNAPPABLE, track );
  587. addAnchor( track->GetEnd(), CORNER | SNAPPABLE, track );
  588. addAnchor( track->GetCenter(), ORIGIN, track);
  589. break;
  590. }
  591. case PCB_MARKER_T:
  592. case PCB_TARGET_T:
  593. addAnchor( aItem->GetPosition(), ORIGIN | CORNER | SNAPPABLE, aItem );
  594. break;
  595. case PCB_VIA_T:
  596. if( aFrom )
  597. {
  598. if( aSelectionFilter && !aSelectionFilter->vias )
  599. break;
  600. }
  601. else
  602. {
  603. if( m_magneticSettings->tracks != MAGNETIC_OPTIONS::CAPTURE_ALWAYS )
  604. break;
  605. }
  606. addAnchor( aItem->GetPosition(), ORIGIN | CORNER | SNAPPABLE, aItem );
  607. break;
  608. case PCB_ZONE_T:
  609. {
  610. if( aFrom && aSelectionFilter && !aSelectionFilter->zones )
  611. break;
  612. const SHAPE_POLY_SET* outline = static_cast<const ZONE*>( aItem )->Outline();
  613. SHAPE_LINE_CHAIN lc;
  614. lc.SetClosed( true );
  615. for( auto iter = outline->CIterateWithHoles(); iter; iter++ )
  616. {
  617. addAnchor( *iter, CORNER | SNAPPABLE, aItem );
  618. lc.Append( *iter );
  619. }
  620. addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem );
  621. break;
  622. }
  623. case PCB_FP_ZONE_T:
  624. {
  625. if( aFrom && aSelectionFilter && !aSelectionFilter->zones )
  626. break;
  627. const SHAPE_POLY_SET* outline = static_cast<const FP_ZONE*>( aItem )->Outline();
  628. SHAPE_LINE_CHAIN lc;
  629. lc.SetClosed( true );
  630. for( auto iter = outline->CIterateWithHoles(); iter; iter++ )
  631. {
  632. addAnchor( *iter, CORNER | SNAPPABLE, aItem );
  633. lc.Append( *iter );
  634. }
  635. addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem );
  636. break;
  637. }
  638. case PCB_DIM_ALIGNED_T:
  639. case PCB_DIM_ORTHOGONAL_T:
  640. case PCB_FP_DIM_ALIGNED_T:
  641. case PCB_FP_DIM_ORTHOGONAL_T:
  642. {
  643. if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
  644. break;
  645. const PCB_DIM_ALIGNED* dim = static_cast<const PCB_DIM_ALIGNED*>( aItem );
  646. addAnchor( dim->GetCrossbarStart(), CORNER | SNAPPABLE, aItem );
  647. addAnchor( dim->GetCrossbarEnd(), CORNER | SNAPPABLE, aItem );
  648. addAnchor( dim->GetStart(), CORNER | SNAPPABLE, aItem );
  649. addAnchor( dim->GetEnd(), CORNER | SNAPPABLE, aItem );
  650. break;
  651. }
  652. case PCB_DIM_CENTER_T:
  653. case PCB_FP_DIM_CENTER_T:
  654. {
  655. if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
  656. break;
  657. const PCB_DIM_CENTER* dim = static_cast<const PCB_DIM_CENTER*>( aItem );
  658. addAnchor( dim->GetStart(), CORNER | SNAPPABLE, aItem );
  659. addAnchor( dim->GetEnd(), CORNER | SNAPPABLE, aItem );
  660. VECTOR2I start( dim->GetStart() );
  661. VECTOR2I radial( dim->GetEnd() - dim->GetStart() );
  662. for( int i = 0; i < 2; i++ )
  663. {
  664. RotatePoint( radial, -ANGLE_90 );
  665. addAnchor( start + radial, CORNER | SNAPPABLE, aItem );
  666. }
  667. break;
  668. }
  669. case PCB_DIM_RADIAL_T:
  670. case PCB_FP_DIM_RADIAL_T:
  671. {
  672. if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
  673. break;
  674. const PCB_DIM_RADIAL* radialDim = static_cast<const PCB_DIM_RADIAL*>( aItem );
  675. addAnchor( radialDim->GetStart(), CORNER | SNAPPABLE, aItem );
  676. addAnchor( radialDim->GetEnd(), CORNER | SNAPPABLE, aItem );
  677. addAnchor( radialDim->GetKnee(), CORNER | SNAPPABLE, aItem );
  678. addAnchor( radialDim->Text().GetPosition(), CORNER | SNAPPABLE, aItem );
  679. break;
  680. }
  681. case PCB_DIM_LEADER_T:
  682. case PCB_FP_DIM_LEADER_T:
  683. {
  684. if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
  685. break;
  686. const PCB_DIM_LEADER* leader = static_cast<const PCB_DIM_LEADER*>( aItem );
  687. addAnchor( leader->GetStart(), CORNER | SNAPPABLE, aItem );
  688. addAnchor( leader->GetEnd(), CORNER | SNAPPABLE, aItem );
  689. addAnchor( leader->Text().GetPosition(), CORNER | SNAPPABLE, aItem );
  690. break;
  691. }
  692. case PCB_FP_TEXT_T:
  693. case PCB_TEXT_T:
  694. if( aFrom && aSelectionFilter && !aSelectionFilter->text )
  695. break;
  696. addAnchor( aItem->GetPosition(), ORIGIN, aItem );
  697. break;
  698. case PCB_GROUP_T:
  699. {
  700. const PCB_GROUP* group = static_cast<const PCB_GROUP*>( aItem );
  701. for( BOARD_ITEM* item : group->GetItems() )
  702. computeAnchors( item, aRefPos, aFrom );
  703. break;
  704. }
  705. default:
  706. break;
  707. }
  708. }
  709. PCB_GRID_HELPER::ANCHOR* PCB_GRID_HELPER::nearestAnchor( const VECTOR2I& aPos, int aFlags,
  710. LSET aMatchLayers )
  711. {
  712. double minDist = std::numeric_limits<double>::max();
  713. ANCHOR* best = nullptr;
  714. for( ANCHOR& a : m_anchors )
  715. {
  716. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( a.item );
  717. if( ( aMatchLayers & item->GetLayerSet() ) == 0 )
  718. continue;
  719. if( ( aFlags & a.flags ) != aFlags )
  720. continue;
  721. double dist = a.Distance( aPos );
  722. if( dist < minDist )
  723. {
  724. minDist = dist;
  725. best = &a;
  726. }
  727. }
  728. return best;
  729. }