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