KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_grid_helper.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2014 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Tomasz Wlostowski <[email protected]>
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, see <https://www.gnu.org/licenses/>.
20 */
21
22#include "pcb_grid_helper.h"
23
24#include <functional>
25#include <algorithm>
26#include <cmath>
27#include <unordered_set>
28
29#include <advanced_config.h>
30#include <board_item.h>
31#include <pcb_dimension.h>
32#include <pcb_drill_map.h>
33#include <pcb_shape.h>
34#include <footprint.h>
35#include <pcb_table.h>
36#include <pad.h>
37#include <pcb_group.h>
38#include <pcb_point.h>
39#include <pcb_barcode.h>
40#include <pcb_reference_image.h>
41#include <pcb_track.h>
42#include <pcb_grid_item.h>
43#include <zone.h>
47#include <geometry/nearest.h>
48#include <geometry/oval.h>
51#include <geometry/shape_rect.h>
55#include <macros.h>
56#include <math/util.h> // for KiROUND
57#include <gal/painter.h>
59#include <pcb_base_frame.h>
60#include <pcbnew_settings.h>
62#include <snap/snap_inference.h>
63#include <tool/snap_frame.h>
64#include <tool/tool_manager.h>
65#include <view/view.h>
66#include <trace_helpers.h>
67
68namespace
69{
79std::optional<int64_t> FindSquareDistanceToItem( const BOARD_ITEM& item, const VECTOR2I& aPos )
80{
81 std::optional<INTERSECTABLE_GEOM> intersectable = BoardItemIntersectable( item );
82 std::optional<NEARABLE_GEOM> nearable;
83
84 if( intersectable )
85 {
86 // Exploit the intersectable as a nearable
87 std::visit(
88 [&]( const auto& geom )
89 {
90 nearable = NEARABLE_GEOM( geom );
91 },
92 *intersectable );
93 }
94
95 // Whatever the item is, we don't have a nearable for it
96 if( !nearable )
97 return std::nullopt;
98
99 const VECTOR2I nearestPt = GetNearestPoint( *nearable, aPos );
100 return nearestPt.SquaredDistance( aPos );
101}
102
103
104VECTOR2I SnapToGrid( const PCB_GRID_ITEM* aGrid, const VECTOR2I& aWorld )
105{
106 const VECTOR2D snapped = aGrid->AsGridGeometry().Snap( VECTOR2D( aWorld ) );
107 return VECTOR2I( KiROUND( snapped.x ), KiROUND( snapped.y ) );
108}
109
110} // namespace
111
117
118
120 GRID_HELPER( aToolMgr, LAYER_ANCHOR ),
121 m_magneticSettings( aMagneticSettings )
122{
123 if( !m_toolMgr )
124 return;
125
126 KIGFX::VIEW* view = m_toolMgr->GetView();
127 KIGFX::RENDER_SETTINGS* settings = view->GetPainter()->GetSettings();
128 KIGFX::COLOR4D auxItemsColor = settings->GetLayerColor( LAYER_AUX_ITEMS );
129 KIGFX::COLOR4D anchorColor = settings->GetLayerColor( LAYER_ANCHOR );
130
131 m_viewAxis.SetSize( 20000 );
133 m_viewAxis.SetColor( auxItemsColor.WithAlpha( 0.4 ) );
134 m_viewAxis.SetDrawAtZero( true );
135 view->Add( &m_viewAxis );
136 view->SetVisible( &m_viewAxis, false );
137
138 m_viewSnapPoint.SetSize( 10 );
140 m_viewSnapPoint.SetColor( auxItemsColor );
141 m_viewSnapPoint.SetDrawAtZero( true );
142 view->Add( &m_viewSnapPoint );
143 getSnapManager().SetSnapGuideColors( anchorColor, anchorColor.Brightened( 0.2 ) );
144 view->SetVisible( &m_viewSnapPoint, false );
145
146 if( m_toolMgr->GetModel() )
147 static_cast<BOARD*>( aToolMgr->GetModel() )->AddListener( this );
148}
149
150
152{
153 if( !m_toolMgr )
154 return;
155
156 KIGFX::VIEW* view = m_toolMgr->GetView();
157
158 view->Remove( &m_viewAxis );
159 view->Remove( &m_viewSnapPoint );
160
161 if( m_toolMgr->GetModel() )
162 static_cast<BOARD*>( m_toolMgr->GetModel() )->RemoveListener( this );
163}
164
165
166void PCB_GRID_HELPER::AddConstructionItems( std::vector<BOARD_ITEM*> aItems, bool aExtensionOnly, bool aIsPersistent )
167{
169 return;
170
171 if( !ADVANCED_CFG::GetCfg().m_EnableExtensionSnaps )
172 return;
173
174 if( !snapInferenceSettings().constructionExtensions )
175 return;
176
177 // For all the elements that get drawn construction geometry,
178 // add something suitable to the construction helper.
179 // This can be nothing.
180 auto constructionItemsBatch = std::make_unique<CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM_BATCH>();
181
182 std::vector<VECTOR2I> referenceOnlyPoints;
183
184 for( BOARD_ITEM* item : aItems )
185 {
186 std::vector<KIGFX::CONSTRUCTION_GEOM::DRAWABLE> constructionDrawables;
187
188 switch( item->Type() )
189 {
190 case PCB_SHAPE_T:
191 {
192 PCB_SHAPE& shape = static_cast<PCB_SHAPE&>( *item );
193
194 switch( shape.GetShape() )
195 {
196 case SHAPE_T::SEGMENT:
197 {
198 if( !aExtensionOnly )
199 {
200 constructionDrawables.emplace_back( LINE{ shape.GetStart(), shape.GetEnd() } );
201 }
202 else
203 {
204 // Two rays, extending from the segment ends
205 const VECTOR2I segVec = shape.GetEnd() - shape.GetStart();
206 constructionDrawables.emplace_back( HALF_LINE{ shape.GetStart(), shape.GetStart() - segVec } );
207 constructionDrawables.emplace_back( HALF_LINE{ shape.GetEnd(), shape.GetEnd() + segVec } );
208 }
209
210 if( aIsPersistent )
211 {
212 // include the original endpoints as construction items
213 // (this allows H/V snapping)
214 constructionDrawables.emplace_back( shape.GetStart() );
215 constructionDrawables.emplace_back( shape.GetEnd() );
216
217 // But mark them as references, so they don't get snapped to themsevles
218 referenceOnlyPoints.emplace_back( shape.GetStart() );
219 referenceOnlyPoints.emplace_back( shape.GetEnd() );
220 }
221 break;
222 }
223 case SHAPE_T::ARC:
224 {
225 if( !aExtensionOnly )
226 {
227 constructionDrawables.push_back( CIRCLE{ shape.GetCenter(), shape.GetRadius() } );
228 }
229 else
230 {
231 // The rest of the circle is the arc through the opposite point to the midpoint
232 const VECTOR2I oppositeMid = shape.GetCenter() + ( shape.GetCenter() - shape.GetArcMid() );
233 constructionDrawables.push_back( SHAPE_ARC{ shape.GetStart(), oppositeMid, shape.GetEnd(), 0 } );
234 }
235
236 constructionDrawables.push_back( shape.GetCenter() );
237
238 if( aIsPersistent )
239 {
240 // include the original endpoints as construction items
241 // (this allows H/V snapping)
242 constructionDrawables.emplace_back( shape.GetStart() );
243 constructionDrawables.emplace_back( shape.GetEnd() );
244
245 // But mark them as references, so they don't get snapped to themselves
246 referenceOnlyPoints.emplace_back( shape.GetStart() );
247 referenceOnlyPoints.emplace_back( shape.GetEnd() );
248 }
249
250 break;
251 }
252 case SHAPE_T::CIRCLE:
254 {
255 constructionDrawables.push_back( shape.GetCenter() );
256 break;
257 }
258 case SHAPE_T::ELLIPSE:
260 {
261 constructionDrawables.push_back( shape.GetEllipseCenter() );
262 break;
263 }
264 default:
265 // This shape doesn't have any construction geometry to draw
266 break;
267 }
268 break;
269 }
271 {
272 const PCB_REFERENCE_IMAGE& pcbRefImg = static_cast<PCB_REFERENCE_IMAGE&>( *item );
273 const REFERENCE_IMAGE& refImg = pcbRefImg.GetReferenceImage();
274
275 constructionDrawables.push_back( refImg.GetPosition() );
276
277 if( refImg.GetTransformOriginOffset() != VECTOR2I( 0, 0 ) )
278 constructionDrawables.push_back( refImg.GetPosition() + refImg.GetTransformOriginOffset() );
279
280 for( const SEG& seg : KIGEOM::BoxToSegs( refImg.GetBoundingBox() ) )
281 constructionDrawables.push_back( seg );
282
283 break;
284 }
285 default:
286 // This item doesn't have any construction geometry to draw
287 break;
288 }
289
290 // At this point, constructionDrawables can be empty, which is fine
291 // (it means there's no additional construction geometry to draw, but
292 // the item is still going to be proposed for activation)
293
294 // Convert the drawables to DRAWABLE_ENTRY format
295 std::vector<CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM::DRAWABLE_ENTRY> drawableEntries;
296 drawableEntries.reserve( constructionDrawables.size() );
297 for( auto& drawable : constructionDrawables )
298 {
299 drawableEntries.emplace_back(
301 }
302
303 constructionItemsBatch->emplace_back( CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM{
305 item,
306 std::move( drawableEntries ),
307 } );
308 }
309
310 if( referenceOnlyPoints.size() )
311 getSnapManager().SetReferenceOnlyPoints( std::move( referenceOnlyPoints ) );
312
313 // Let the manager handle it
314 getSnapManager().GetConstructionManager().ProposeConstructionItems( std::move( constructionItemsBatch ),
315 aIsPersistent );
316}
317
318
320{
321 if( !canUseGrid() )
322 return GRID_HELPER::Align( aPoint, aGrid );
323
324 BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
325
326 // Hidden grid items don't snap the cursor (placement/routing keep
327 // following them — geometry tools follow data, not display).
328 if( !board->IsElementVisible( LAYER_SUBGRIDS ) )
329 return GRID_HELPER::Align( aPoint, aGrid );
330
331 // Priority + coverage-area resolution for the active CURSOR grid lives in
332 // FindActiveGridAt; if one covers aPoint, snap exclusively to that grid.
333 if( PCB_GRID_ITEM* active = FindActiveGridAt( *board, aPoint, PCB_GRID_ROLE::CURSOR ) )
334 return SnapToGrid( active, aPoint );
335
336 // No active grid covers aPoint - fall back to the display grid, but let any
337 // nearby CURSOR-role grid contribute snap candidates within snapRange.
338 const VECTOR2I gridAligned = GRID_HELPER::Align( aPoint, aGrid );
339
340 const int snapSize = 25;
341 double snapScreen = m_toolMgr->GetView()->ToWorld( snapSize );
342 int snapRange = KiROUND( std::min( snapScreen, GetVisibleGrid().x ) );
343 SEG::ecoord bestDist = SEG::Square( snapRange );
344 VECTOR2I best = gridAligned;
345
346 for( BOARD_ITEM* item : board->Drawings() )
347 {
348 if( item->Type() != PCB_GRID_ITEM_T )
349 continue;
350
351 PCB_GRID_ITEM* grid = static_cast<PCB_GRID_ITEM*>( item );
352
353 if( !grid->Affects().cursor )
354 continue;
355
356 if( grid->IsSelected() )
357 {
358 continue;
359 }
360
361 BOX2I bbox = grid->GetBoundingBox();
362 bbox.Inflate( snapRange );
363
364 if( !bbox.Contains( aPoint ) )
365 continue;
366
367 const VECTOR2I candidate = SnapToGrid( grid, aPoint );
368
369 const SEG::ecoord dist = ( candidate - aPoint ).SquaredEuclideanNorm();
370
371 if( dist < bestDist )
372 {
373 bestDist = dist;
374 best = candidate;
375 }
376 }
377
378 return best;
379}
380
381
383{
384 const int c_gridSnapEpsilon_sq = 4;
385
386 VECTOR2I aligned = Align( aPoint );
387
388 if( !m_enableSnap )
389 return aligned;
390
391 std::vector<VECTOR2I> points;
392
393 const SEG testSegments[] = { SEG( aligned, aligned + VECTOR2( 1, 0 ) ),
394 SEG( aligned, aligned + VECTOR2( 0, 1 ) ),
395 SEG( aligned, aligned + VECTOR2( 1, 1 ) ),
396 SEG( aligned, aligned + VECTOR2( 1, -1 ) ) };
397
398 for( const SEG& seg : testSegments )
399 {
400 OPT_VECTOR2I vec = aSeg.IntersectLines( seg );
401
402 if( vec && aSeg.SquaredDistance( *vec ) <= c_gridSnapEpsilon_sq )
403 points.push_back( *vec );
404 }
405
406 VECTOR2I nearest = aligned;
408
409 // Snap by distance between pointer and endpoints
410 for( const VECTOR2I& pt : { aSeg.A, aSeg.B } )
411 {
412 SEG::ecoord d_sq = ( pt - aPoint ).SquaredEuclideanNorm();
413
414 if( d_sq < min_d_sq )
415 {
416 min_d_sq = d_sq;
417 nearest = pt;
418 }
419 }
420
421 // Snap by distance between aligned cursor and intersections
422 for( const VECTOR2I& pt : points )
423 {
424 SEG::ecoord d_sq = ( pt - aligned ).SquaredEuclideanNorm();
425
426 if( d_sq < min_d_sq )
427 {
428 min_d_sq = d_sq;
429 nearest = pt;
430 }
431 }
432
433 return nearest;
434}
435
436
438{
439 VECTOR2I aligned = Align( aPoint );
440
441 if( !m_enableSnap )
442 return aligned;
443
444 std::vector<VECTOR2I> points;
445
446 aArc.IntersectLine( SEG( aligned, aligned + VECTOR2( 1, 0 ) ), &points );
447 aArc.IntersectLine( SEG( aligned, aligned + VECTOR2( 0, 1 ) ), &points );
448 aArc.IntersectLine( SEG( aligned, aligned + VECTOR2( 1, 1 ) ), &points );
449 aArc.IntersectLine( SEG( aligned, aligned + VECTOR2( 1, -1 ) ), &points );
450
451 VECTOR2I nearest = aligned;
453
454 // Snap by distance between pointer and endpoints
455 for( const VECTOR2I& pt : { aArc.GetP0(), aArc.GetP1() } )
456 {
457 SEG::ecoord d_sq = ( pt - aPoint ).SquaredEuclideanNorm();
458
459 if( d_sq < min_d_sq )
460 {
461 min_d_sq = d_sq;
462 nearest = pt;
463 }
464 }
465
466 // Snap by distance between aligned cursor and intersections
467 for( const VECTOR2I& pt : points )
468 {
469 SEG::ecoord d_sq = ( pt - aligned ).SquaredEuclideanNorm();
470
471 if( d_sq < min_d_sq )
472 {
473 min_d_sq = d_sq;
474 nearest = pt;
475 }
476 }
477
478 return nearest;
479}
480
481
482VECTOR2I PCB_GRID_HELPER::SnapToPad( const VECTOR2I& aMousePos, std::deque<PAD*>& aPads )
483{
484 wxLogTrace( traceSnap, "SnapToPad: mouse pos (%d, %d), pads count: %zu", aMousePos.x, aMousePos.y, aPads.size() );
485 clearAnchors();
486
487 for( BOARD_ITEM* item : aPads )
488 {
489 if( item->HitTest( aMousePos ) )
490 computeAnchors( item, aMousePos, true, nullptr );
491 }
492
493 double minDist = std::numeric_limits<double>::max();
494 ANCHOR* nearestOrigin = nullptr;
495
496 for( ANCHOR& a : m_anchors )
497 {
498 if( ( ORIGIN & a.flags ) != ORIGIN )
499 continue;
500
501 double dist = a.Distance( aMousePos );
502
503 if( dist < minDist )
504 {
505 minDist = dist;
506 nearestOrigin = &a;
507 }
508 }
509
510 return nearestOrigin ? nearestOrigin->pos : aMousePos;
511}
512
513
515{
516 // If the item being removed is involved in the snap, clear the snap item
517 if( m_snapItem )
518 {
519 for( EDA_ITEM* eda_item : m_snapItem->items )
520 {
521 if( eda_item->IsBOARD_ITEM() )
522 {
523 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( eda_item );
524
525 if( item == aRemovedItem || item->GetParentFootprint() == aRemovedItem )
526 {
527 m_snapItem = std::nullopt;
528 break;
529 }
530 }
531 }
532 }
533}
534
535
536void PCB_GRID_HELPER::OnBoardItemsRemoved( BOARD& aBoard, std::vector<BOARD_ITEM*>& aBoardItems )
537{
538 // This is a bulk-remove. Simply clearing the snap item will be the most performant.
539 m_snapItem = std::nullopt;
540}
541
542
544{
545 if( aItem.Type() == PCB_FOOTPRINT_T )
546 return static_cast<const FOOTPRINT&>( aItem ).GetBoundingBox( false );
547
548 return aItem.GetBoundingBox();
549}
550
551
553{
554 if( !m_toolMgr )
555 return false;
556
557 // Keyed off the board rather than the current tool: PCB_TOOL_BASE lives in the pcbnew
558 // kiface, so casting to it from pcbcommon leaves cvpcb with an undefined typeinfo
559 const BOARD* board = static_cast<const BOARD*>( m_toolMgr->GetModel() );
560
561 return board && board->IsFootprintHolder();
562}
563
564
566{
568
569 // The caller's own switch wins over the user's preference, so apply it after the read.
570 auto applyOverride =
572 {
574 settings.constructionExtensions = false;
575
576 return settings;
577 };
578
579 if( !m_toolMgr )
580 return applyOverride();
581
582 if( PCB_BASE_FRAME* frame = dynamic_cast<PCB_BASE_FRAME*>( m_toolMgr->GetToolHolder() ) )
583 {
585 {
586 if( FOOTPRINT_EDITOR_SETTINGS* cfg = frame->GetFootprintEditorSettings() )
587 settings = cfg->m_SnapInference;
588 }
589 else if( PCBNEW_SETTINGS* cfg = frame->GetPcbNewSettings() )
590 {
591 settings = cfg->m_SnapInference;
592 }
593 }
594 else if( PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( m_toolMgr->GetSettings() ) )
595 {
596 // Headless callers have settings but no PCB frame.
597 settings = cfg->m_SnapInference;
598 }
599
600 return applyOverride();
601}
602
603
604VECTOR2I PCB_GRID_HELPER::BestDragOrigin( const VECTOR2I& aMousePos, std::vector<BOARD_ITEM*>& aItems,
605 GRID_HELPER_GRIDS aGrid,
606 const PCB_SELECTION_FILTER_OPTIONS* aSelectionFilter )
607{
608 wxLogTrace( traceSnap, "BestDragOrigin: mouse pos (%d, %d), items count: %zu", aMousePos.x, aMousePos.y,
609 aItems.size() );
610 clearAnchors();
611
612 computeAnchors( aItems, aMousePos, true, aSelectionFilter, nullptr, true );
613
614 double lineSnapMinCornerDistance = m_toolMgr->GetView()->ToWorld( 50 );
615
616 ANCHOR* nearestOutline = nearestAnchor( aMousePos, OUTLINE );
617 ANCHOR* nearestCorner = nearestAnchor( aMousePos, CORNER );
618 ANCHOR* nearestOrigin = nearestAnchor( aMousePos, ORIGIN );
619 ANCHOR* best = nullptr;
620 double minDist = std::numeric_limits<double>::max();
621
622 if( nearestOrigin )
623 {
624 minDist = nearestOrigin->Distance( aMousePos );
625 best = nearestOrigin;
626
627 wxLogTrace( traceSnap, " nearest origin winning at (%d, %d), distance=%f", nearestOrigin->pos.x,
628 nearestOrigin->pos.y, minDist );
629 }
630
631 if( nearestCorner )
632 {
633 double dist = nearestCorner->Distance( aMousePos );
634
635 if( dist < minDist )
636 {
637 minDist = dist;
638 best = nearestCorner;
639
640 wxLogTrace( traceSnap, " nearest corner winning at (%d, %d), distance=%f", nearestCorner->pos.x,
641 nearestCorner->pos.y, dist );
642 }
643 }
644
645 if( nearestOutline )
646 {
647 double dist = nearestOutline->Distance( aMousePos );
648
649 if( minDist > lineSnapMinCornerDistance && dist < minDist )
650 {
651 best = nearestOutline;
652
653 wxLogTrace( traceSnap, " nearest outline winning at (%d, %d), distance=%f", nearestOutline->pos.x,
654 nearestOutline->pos.y, dist );
655 }
656 }
657
658 VECTOR2I ret = best ? best->pos : aMousePos;
659
660 if( best )
661 {
662 std::optional<BOX2I> movingBounds;
663
664 for( BOARD_ITEM* item : aItems )
665 {
666 if( !item )
667 continue;
668
669 if( movingBounds )
670 movingBounds->Merge( layoutBounds( *item ) );
671 else
672 movingBounds = layoutBounds( *item );
673 }
674
675 bool padCenter = ( best->pointTypes & POINT_TYPE::PT_CENTER )
676 && std::any_of( best->items.begin(), best->items.end(),
677 []( const EDA_ITEM* aItem )
678 {
679 return aItem && aItem->Type() == PCB_PAD_T;
680 } );
681
682 setLayoutReference( ret, movingBounds, padCenter );
683 }
684 else
685 {
686 setLayoutReference( ret, std::nullopt, false );
687 }
688
689 wxLogTrace( traceSnap, " have best: %s, returning (%d, %d)", best ? "yes" : "no", ret.x, ret.y );
690 return ret;
691}
692
693
695{
696 LSET layers;
697 std::vector<BOARD_ITEM*> item;
698
699 if( aReferenceItem )
700 {
701 layers = aReferenceItem->GetLayerSet();
702 item.push_back( aReferenceItem );
703 }
704 else if( PCB_BASE_FRAME* frame = dynamic_cast<PCB_BASE_FRAME*>( m_toolMgr->GetToolHolder() );
705 frame && frame->GetScreen() )
706 {
707 layers = LSET( { frame->GetActiveLayer() } );
708 }
709 else
710 {
711 layers = LSET::AllLayersMask();
712 }
713
714 return ResolveSnap( aOrigin, layers, aGrid, item );
715}
716
717
719 const std::vector<BOARD_ITEM*>& aSkip,
720 std::optional<VECTOR2I> aMovingReferencePoint )
721{
722 wxLogTrace( traceSnap, "ResolveSnap: origin (%d, %d), enableSnap=%d, enableGrid=%d, enableSnapLine=%d", aOrigin.x,
724
726 const double snapScale = ranges.scale;
727 const int snapRange = ranges.range;
728
729 const SNAP_INFERENCE_SETTINGS inferenceSettings = snapInferenceSettings();
730
731 const bool constructionEnabled =
733
734 if( !constructionEnabled )
736
737 //Respect limits of coordinates representation
738 const BOX2I visibilityHorizon =
739 BOX2ISafe( VECTOR2D( aOrigin ) - snapRange / 2.0, VECTOR2D( snapRange, snapRange ) );
740
741 clearAnchors();
742
743 const std::vector<BOARD_ITEM*> visibleItems = queryVisible( { visibilityHorizon }, aSkip );
744 computeAnchors( visibleItems, aOrigin, false, nullptr, &aLayers, false );
745
746 ANCHOR* nearest = nearestAnchor( aOrigin, SNAPPABLE );
747 VECTOR2I nearestGrid = Align( aOrigin, aGrid );
748 const VECTOR2D gridSize = GetGridSize( aGrid );
749
750 SNAP_SOURCE_CONTEXT context;
752 context.sourcePoint = aOrigin;
753 context.movingReferencePoint = aMovingReferencePoint;
755
756 for( BOARD_ITEM* item : aSkip )
757 {
758 if( !item )
759 continue;
760
761 if( context.movingBounds )
762 context.movingBounds->Merge( layoutBounds( *item ) );
763 else
764 context.movingBounds = layoutBounds( *item );
765 }
766
767 if( aSkip.size() == 1 && aSkip.front() )
768 {
769 BOARD_ITEM* sourceItem = aSkip.front();
771 std::optional<std::pair<VECTOR2I, VECTOR2I>> endpoints;
772
775
776 if( sourceItem->Type() == PCB_TRACE_T )
777 {
778 PCB_TRACK* track = static_cast<PCB_TRACK*>( sourceItem );
779 endpoints = std::pair( track->GetStart(), track->GetEnd() );
780 }
781 else if( sourceItem->Type() == PCB_SHAPE_T )
782 {
783 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( sourceItem );
784
785 if( shape->GetShape() == SHAPE_T::SEGMENT )
786 endpoints = std::pair( shape->GetStart(), shape->GetEnd() );
787 }
788
789 if( endpoints && m_pointEditProfile )
790 {
791 context.stationarySourceLeg =
792 endpoints->first.SquaredDistance( aOrigin ) > endpoints->second.SquaredDistance( aOrigin )
793 ? endpoints->first
794 : endpoints->second;
795 }
796 }
797
798 SNAP_INFERENCE_PROVIDER inferenceProvider;
799 const bool inferenceEnabled = m_enableSnap
800 && ( inferenceSettings.objectGeometry || inferenceSettings.tangentNormal
801 || inferenceSettings.alignmentDistribution );
802 const bool geometryEnabled =
803 m_enableSnap && ( inferenceSettings.objectGeometry || inferenceSettings.tangentNormal );
804
805 if( geometryEnabled && context.movingItem )
806 {
807 for( size_t i = 0; i < m_stationarySelfSegments.size(); ++i )
808 {
809 SNAP_STABLE_ID id =
810 MakeDerivedSnapId( SNAP_ID_KIND::SELF_SEGMENT, *context.movingItem, static_cast<int>( i ) );
811 context.stationarySelfFeatures.push_back( id );
812 inferenceProvider.AddPath( { id, m_stationarySelfSegments[i], false } );
813 }
814 }
815
816 if( inferenceEnabled )
817 {
818 const auto eligibleInferenceTarget = [&]( BOARD_ITEM* aItem )
819 {
820 if( !m_magneticSettings->allLayers && !( aLayers & aItem->GetLayerSet() ).any() )
821 {
822 return false;
823 }
824
825 switch( aItem->Type() )
826 {
827 case PCB_TRACE_T:
829
831
832 default: return m_magneticSettings->graphics;
833 }
834 };
835
836 for( BOARD_ITEM* item : visibleItems )
837 {
838 if( !eligibleInferenceTarget( item ) )
839 continue;
840
841 if( !geometryEnabled )
842 continue;
843
844 std::optional<INTERSECTABLE_GEOM> geometry = BoardItemIntersectable( *item );
845
846 if( !geometry )
847 continue;
848
849 inferenceProvider.AddPath( { { SNAP_ID_KIND::ITEM_GEOMETRY, SnapTargetId( item->m_Uuid ),
850 static_cast<int>( item->Type() ), 0 },
851 std::move( *geometry ),
852 false } );
853 }
854
855 if( inferenceSettings.alignmentDistribution && context.movingBounds )
856 {
857 std::vector<BOARD_ITEM*> layoutItems;
858 const BOX2I viewport = BOX2ISafe( m_toolMgr->GetView()->GetViewport() );
859 BOX2I movingBounds = *context.movingBounds;
860
861 if( context.movingReferencePoint )
862 movingBounds.Offset( context.sourcePoint - *context.movingReferencePoint );
863
864 // Alignment ignores separation along its guide; equal spacing only requires overlap
865 // perpendicular to its axis. Their exact query closure is therefore a cross.
866 BOX2I verticalStrip =
867 BOX2ISafe( VECTOR2D( static_cast<double>( movingBounds.GetLeft() ) - snapRange, viewport.GetTop() ),
868 VECTOR2D( movingBounds.GetWidth() + 2.0 * snapRange, viewport.GetHeight() ) );
869 BOX2I horizontalStrip =
870 BOX2ISafe( VECTOR2D( viewport.GetLeft(), static_cast<double>( movingBounds.GetTop() ) - snapRange ),
871 VECTOR2D( viewport.GetWidth(), movingBounds.GetHeight() + 2.0 * snapRange ) );
872 verticalStrip = verticalStrip.Intersect( viewport );
873 horizontalStrip = horizontalStrip.Intersect( viewport );
874 std::vector<BOARD_ITEM*> visibleLayoutItems = queryVisible( { verticalStrip, horizontalStrip }, aSkip );
875
876 const bool insideFootprint = editingInsideFootprint();
877
878 for( BOARD_ITEM* item : visibleLayoutItems )
879 {
880 FOOTPRINT* footprint = item->GetParentFootprint();
881
882 if( footprint && !insideFootprint )
883 layoutItems.push_back( footprint );
884 else
885 layoutItems.push_back( item );
886 }
887
888 std::sort( layoutItems.begin(), layoutItems.end(), std::less<>() );
889 layoutItems.erase( std::unique( layoutItems.begin(), layoutItems.end() ), layoutItems.end() );
890
891 // Moving items and their containers. A container's bounds enclose what is being
892 // moved, and pads reached through their footprint bypass the queryVisible skip list.
893 std::unordered_set<BOARD_ITEM*> moving( aSkip.begin(), aSkip.end() );
894
895 for( BOARD_ITEM* item : aSkip )
896 {
897 for( FOOTPRINT* parent = item ? item->GetParentFootprint() : nullptr; parent;
898 parent = parent->GetParentFootprint() )
899 {
900 moving.insert( parent );
901 }
902 }
903
904 for( BOARD_ITEM* item : layoutItems )
905 {
906 // Aligning to a container of the move would align the move to itself. The
907 // container's other children remain valid targets.
908 if( !eligibleInferenceTarget( item ) || moving.count( item ) )
909 continue;
910
911 std::optional<SNAP_TARGET_ID> parent;
912
913 if( item->GetParent() )
914 parent = SnapTargetId( item->GetParent()->m_Uuid );
915
916 inferenceProvider.AddBounds( { { SNAP_ID_KIND::ITEM_GEOMETRY, SnapTargetId( item->m_Uuid ),
917 static_cast<int>( item->Type() ), 0 },
918 layoutBounds( *item ),
919 std::move( parent ) } );
920 }
921
922 size_t padCenters = 0;
923
924 const auto addPadCenter = [&]( PAD* aPad )
925 {
926 if( !eligibleInferenceTarget( aPad ) || moving.count( aPad ) )
927 return;
928
929 std::optional<SNAP_TARGET_ID> parent;
930
931 if( FOOTPRINT* footprint = aPad->GetParentFootprint() )
932 parent = SnapTargetId( footprint->m_Uuid );
933
934 inferenceProvider.AddAlignmentPoint( { { SNAP_ID_KIND::INTRINSIC_ANCHOR, SnapTargetId( aPad->m_Uuid ) },
935 aPad->GetPosition(),
936 std::move( parent ) } );
937 ++padCenters;
938 };
939
940 for( BOARD_ITEM* item : layoutItems )
941 {
942 if( item->Type() == PCB_PAD_T )
943 {
944 addPadCenter( static_cast<PAD*>( item ) );
945 }
946 else if( item->Type() == PCB_FOOTPRINT_T )
947 {
948 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
949 addPadCenter( pad );
950 }
951 }
952
953 wxLogTrace( wxT( "KICAD_SNAP_RESOLVER" ), "layout targets=%zu pad-centers=%zu", layoutItems.size(),
954 padCenters );
955 }
956 }
957
958 enum class PRESENTATION_KIND
959 {
960 ANCHOR_MARKER,
961 GUIDE,
962 POINT_ON_ELEMENT
963 };
964
965 struct PRESENTATION
966 {
967 PRESENTATION_KIND kind;
968 std::optional<ANCHOR> anchor;
969 bool proposeConstruction = false;
970 };
971
973 frame.context = context;
977 frame.trace = snapTraceCallback( context );
978 emitAngleBranchCandidates( frame.candidates, aOrigin, snapScale );
979
980 if( m_enableSnap && inferenceSettings.objectGeometry )
981 {
982 for( SNAP_CANDIDATE& candidate : inferenceProvider.CollectObjectGeometry( context, snapRange ) )
983 frame.candidates.push_back( std::move( candidate ) );
984 }
985
986 if( m_enableSnap && inferenceSettings.tangentNormal && context.stationarySourceLeg )
987 {
988 for( SNAP_CANDIDATE& candidate : inferenceProvider.CollectTangentNormal( context, snapRange, true, true ) )
989 frame.candidates.push_back( std::move( candidate ) );
990 }
991
992 if( m_enableSnap && inferenceSettings.alignmentDistribution && context.movingBounds )
993 {
994 std::vector<SNAP_CANDIDATE> alignment = inferenceProvider.CollectAlignment( context, snapRange );
995 std::vector<SNAP_CANDIDATE> spacing = inferenceProvider.CollectEqualSpacing( context, snapRange );
996
997 wxLogTrace( wxT( "KICAD_SNAP_RESOLVER" ), "layout candidates alignment=%zu spacing=%zu", alignment.size(),
998 spacing.size() );
999
1000 for( SNAP_CANDIDATE& candidate : alignment )
1001 frame.candidates.push_back( std::move( candidate ) );
1002
1003 for( SNAP_CANDIDATE& candidate : spacing )
1004 frame.candidates.push_back( std::move( candidate ) );
1005 }
1006
1007 emitSelfAndGridCandidates( frame.candidates, context, aOrigin, nearestGrid, snapScale, snapRange, m_enableGrid );
1008
1009 const int snapIn = ranges.in;
1010 const int snapOut = ranges.out;
1011
1012 wxLogTrace( traceSnap, " snapRange=%d, snapIn=%d, snapOut=%d", snapRange, snapIn, snapOut );
1013 wxLogTrace( traceSnap, " visibleItems count=%zu, anchors count=%zu", visibleItems.size(), m_anchors.size() );
1014 wxLogTrace( traceSnap, " nearest anchor: %s at (%d, %d), distance=%f", nearest ? "found" : "none",
1015 nearest ? nearest->pos.x : 0, nearest ? nearest->pos.y : 0,
1016 nearest ? nearest->Distance( aOrigin ) : -1.0 );
1017 wxLogTrace( traceSnap, " nearestGrid: (%d, %d)", nearestGrid.x, nearestGrid.y );
1018
1020 {
1021 ad->ClearAnchors();
1022
1023 for( const ANCHOR& anchor : m_anchors )
1024 ad->AddAnchor( anchor.pos );
1025
1026 ad->SetNearest( nearest ? OPT_VECTOR2I{ nearest->pos } : std::nullopt );
1027 m_toolMgr->GetView()->Update( ad, KIGFX::GEOMETRY );
1028 }
1029
1030 // The distance to the nearest snap point, if any
1031 std::optional<int> snapDist;
1032
1033 if( nearest )
1034 snapDist = nearest->Distance( aOrigin );
1035
1036 if( m_snapItem )
1037 {
1038 int existingDist = m_snapItem->Distance( aOrigin );
1039 if( !snapDist || existingDist < *snapDist )
1040 snapDist = existingDist;
1041 }
1042
1043 wxLogTrace( traceSnap, " snapDist: %s (value=%d)", snapDist ? "set" : "none", snapDist ? *snapDist : -1 );
1044 wxLogTrace( traceSnap, " m_snapItem: %s", m_snapItem ? "exists" : "none" );
1045
1046 showConstructionGeometry( constructionEnabled );
1047
1048 SNAP_MANAGER& snapManager = getSnapManager();
1049 SNAP_LINE_MANAGER& snapLineManager = snapManager.GetSnapLineManager();
1050
1051 const auto ptIsReferenceOnly = [&]( const VECTOR2I& aPt )
1052 {
1053 const std::vector<VECTOR2I>& referenceOnlyPoints = snapManager.GetReferenceOnlyPoints();
1054 return std::find( referenceOnlyPoints.begin(), referenceOnlyPoints.end(), aPt ) != referenceOnlyPoints.end();
1055 };
1056
1057 const auto proposeConstructionForItems = [&]( const std::vector<EDA_ITEM*>& aItems )
1058 {
1059 // Add any involved item as a temporary construction item
1060 // (de-duplication with existing construction items is handled later)
1061 std::vector<BOARD_ITEM*> items;
1062
1063 for( EDA_ITEM* item : aItems )
1064 {
1065 if( !item->IsBOARD_ITEM() )
1066 continue;
1067
1068 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
1069
1070 // Null items are allowed to arrive here as they represent geometry that isn't
1071 // specifically tied to a board item. For example snap lines from some
1072 // other anchor.
1073 // But they don't produce new construction items.
1074 if( boardItem )
1075 {
1076 if( m_magneticSettings->allLayers || ( ( aLayers & boardItem->GetLayerSet() ).any() ) )
1077 items.push_back( boardItem );
1078 }
1079 }
1080
1081 // Temporary construction items are not persistent and don't
1082 // overlay the items themselves (as the items will not be moved)
1083 if( constructionEnabled )
1084 AddConstructionItems( items, true, false );
1085 };
1086
1087 const auto anchorId = [&]( const ANCHOR& aAnchor )
1088 {
1089 std::vector<SNAP_TARGET_ID> targets;
1090
1091 for( const EDA_ITEM* item : aAnchor.items )
1092 {
1093 if( item )
1094 targets.push_back( SnapTargetId( item->m_Uuid ) );
1095 }
1096
1097 SNAP_ID_KIND kind =
1099 SNAP_STABLE_ID pointId = MakePointSnapId( kind, aAnchor.pos, aAnchor.pointTypes );
1100
1101 if( targets.empty() )
1102 return pointId;
1103
1104 targets.push_back( pointId.target );
1105 return MakeCompositeSnapId( kind, targets, aAnchor.pointTypes );
1106 };
1107
1108 const auto addAnchorCandidate = [&]( const ANCHOR& aAnchor, bool aRetained )
1109 {
1110 SNAP_STABLE_ID id = anchorId( aAnchor );
1111
1112 if( frame.presentation.contains( id ) )
1113 {
1114 if( aRetained )
1115 frame.retainedId = id;
1116
1117 return;
1118 }
1119
1120 const bool constructed = aAnchor.flags & CONSTRUCTED;
1124 aAnchor.pos, aAnchor.Distance( aOrigin ) / snapScale ) );
1125 frame.presentation.emplace(
1126 id, PRESENTATION{ PRESENTATION_KIND::ANCHOR_MARKER, aAnchor, !aRetained && !constructed } );
1127
1128 if( aRetained )
1129 frame.retainedId = id;
1130 };
1131
1132 // Hover activation, snap-line suppression and anchor acceptance are all the same question.
1133 const bool nearestCaptured = nearest && nearest->Distance( aOrigin ) <= snapIn;
1134 bool keepConstructionProposal = false;
1135 bool allowHoverActivation = false;
1136
1137 if( m_enableSnap )
1138 {
1139 wxLogTrace( traceSnap, " Snap enabled, checking snap options..." );
1140 allowHoverActivation = !nearestCaptured;
1141
1142 if( m_enableSnapLine )
1143 {
1144 wxLogTrace( traceSnap, " Checking snap lines..." );
1145
1146 OPT_VECTOR2I snapLineSnap = snapLineManager.GetNearestSnapLinePoint( aOrigin, nearestGrid, snapDist,
1147 snapRange, gridSize, GetOrigin() );
1148
1149 if( !snapLineSnap && constructionEnabled )
1150 {
1151 std::optional<VECTOR2I> constructionSnap =
1152 SnapToConstructionLines( aOrigin, nearestGrid, gridSize, snapRange );
1153
1154 if( constructionSnap )
1155 snapLineSnap = *constructionSnap;
1156 }
1157
1158 if( snapLineSnap && m_skipPoint != *snapLineSnap )
1159 {
1160 wxLogTrace( traceSnap, " Snap line found at (%d, %d)", snapLineSnap->x, snapLineSnap->y );
1161
1162 if( !nearestCaptured )
1163 {
1164 if( !ptIsReferenceOnly( *snapLineSnap ) )
1165 {
1167 frame.candidates.push_back( SNAP_CANDIDATE::Point(
1169 *snapLineSnap, snapLineSnap->Distance( aOrigin ) / snapScale ) );
1170 frame.presentation.emplace( id, PRESENTATION{ PRESENTATION_KIND::GUIDE, std::nullopt, false } );
1171 }
1172 else
1173 {
1174 wxLogTrace( traceSnap, " Snap line point is reference-only, continuing..." );
1175 keepConstructionProposal = true;
1176 }
1177 }
1178 }
1179 }
1180
1181 if( m_snapItem )
1182 {
1183 int dist = m_snapItem->Distance( aOrigin );
1184
1185 wxLogTrace( traceSnap, " Checking existing m_snapItem, dist=%d (snapOut=%d)", dist, snapOut );
1186
1187 if( dist <= snapOut && !ptIsReferenceOnly( m_snapItem->pos ) )
1188 {
1189 if( nearest && ptIsReferenceOnly( nearest->pos ) && nearest->Distance( aOrigin ) <= snapRange )
1190 snapLineManager.SetSnapLineOrigin( nearest->pos );
1191
1192 addAnchorCandidate( *m_snapItem, true );
1193 }
1194 }
1195
1196 if( nearestCaptured )
1197 {
1198 wxLogTrace( traceSnap, " Nearest anchor within snapIn range" );
1199
1200 if( ptIsReferenceOnly( nearest->pos ) )
1201 {
1202 wxLogTrace( traceSnap, " Nearest anchor is reference-only, setting snap line origin" );
1203 snapLineManager.SetSnapLineOrigin( nearest->pos );
1204 keepConstructionProposal = true;
1205 }
1206 else
1207 {
1208 addAnchorCandidate( *nearest, false );
1209 }
1210 }
1211
1212 if( !m_enableGrid )
1213 {
1214 wxLogTrace( traceSnap, " Grid disabled, checking point-on-element snap..." );
1215
1216 OPT_VECTOR2I nearestPointOnAnElement = GetNearestPoint( m_pointOnLineCandidates, aOrigin );
1217
1218 if( nearestPointOnAnElement && nearestPointOnAnElement->Distance( aOrigin ) <= snapRange )
1219 {
1220 SNAP_STABLE_ID id = MakePointSnapId( SNAP_ID_KIND::ITEM_GEOMETRY, *nearestPointOnAnElement );
1221 frame.candidates.push_back( SNAP_CANDIDATE::Point(
1223 *nearestPointOnAnElement, nearestPointOnAnElement->Distance( aOrigin ) / snapScale ) );
1224 frame.presentation.emplace( id,
1225 PRESENTATION{ PRESENTATION_KIND::POINT_ON_ELEMENT, std::nullopt, false } );
1226 }
1227 }
1228 }
1229
1230 // Object retention wins because its tier already outranks angle restriction.
1231 if( !frame.retainedId && m_retainedAngleBranch )
1233
1234 // A caller that reads meaning from where between two items the pointer lands cannot use
1235 // the snaps that sit exactly between them.
1236 if( !m_suppressedSnapSubtypes.empty() )
1237 {
1238 std::erase_if( frame.candidates,
1239 [&]( const SNAP_CANDIDATE& aCandidate )
1240 {
1241 return m_suppressedSnapSubtypes.contains( aCandidate.subtype );
1242 } );
1243 }
1244
1245 SNAP_FRAME_OUTPUT<PRESENTATION> output = ResolveSnapFrame( std::move( frame ) );
1246 SNAP_RESULT& result = output.result;
1248
1249 m_snapItem = std::nullopt;
1250 snapLineManager.SetSnapLineEnd( std::nullopt );
1251 bool suppressHoverActivation = false;
1252
1253 if( output.presentation )
1254 {
1255 const PRESENTATION& presentation = output.presentation->payload;
1256 keepConstructionProposal = true;
1257
1258 if( presentation.kind == PRESENTATION_KIND::ANCHOR_MARKER && presentation.anchor )
1259 {
1260 suppressHoverActivation = true;
1261 m_snapItem = *presentation.anchor;
1262 snapLineManager.SetSnappedAnchor( m_snapItem->pos );
1263 updateSnapPoint( { m_snapItem->pos, m_snapItem->pointTypes } );
1264
1265 if( presentation.proposeConstruction )
1266 proposeConstructionForItems( m_snapItem->items );
1267 }
1268 else if( presentation.kind == PRESENTATION_KIND::GUIDE )
1269 {
1270 suppressHoverActivation = true;
1271 snapLineManager.SetSnapLineEnd( result.position );
1272 m_viewSnapPoint.SetSnapTypes( POINT_TYPE::PT_NONE );
1273 m_toolMgr->GetView()->SetVisible( &m_viewSnapPoint, false );
1274 }
1275 else if( presentation.kind == PRESENTATION_KIND::POINT_ON_ELEMENT )
1276 {
1278 }
1279 }
1280 else
1281 {
1282 m_toolMgr->GetView()->SetVisible( &m_viewSnapPoint, false );
1283 }
1284
1286
1287 static const bool canActivateByHitTest = ADVANCED_CFG::GetCfg().m_ExtensionSnapActivateOnHover;
1288
1289 if( constructionEnabled && canActivateByHitTest && allowHoverActivation && !suppressHoverActivation )
1290 {
1291 for( BOARD_ITEM* item : visibleItems )
1292 {
1293 if( item->HitTest( aOrigin, 0 ) )
1294 {
1295 proposeConstructionForItems( { item } );
1296 keepConstructionProposal = true;
1297 break;
1298 }
1299 }
1300 }
1301
1302 if( !keepConstructionProposal )
1303 snapManager.GetConstructionManager().CancelProposal();
1304
1305 return result;
1306}
1307
1308
1310{
1311 if( !m_snapItem )
1312 return nullptr;
1313
1314 // The snap anchor doesn't have an item associated with it
1315 // (odd, could it be entirely made of construction geometry?)
1316 if( m_snapItem->items.empty() )
1317 return nullptr;
1318
1319 return static_cast<BOARD_ITEM*>( m_snapItem->items[0] );
1320}
1321
1322
1324{
1325 m_snapItem = std::nullopt;
1328 manager.ClearSnapLine();
1329 m_toolMgr->GetView()->SetVisible( &m_viewSnapPoint, false );
1330}
1331
1332
1334{
1335 if( !aItem )
1336 return GRID_CURRENT;
1337
1338 switch( aItem->Type() )
1339 {
1340 case PCB_FOOTPRINT_T:
1341 case PCB_PAD_T:
1342 return GRID_CONNECTABLE;
1343
1344 case PCB_TEXT_T:
1345 case PCB_FIELD_T:
1346 return GRID_TEXT;
1347
1348 case PCB_SHAPE_T:
1349 case PCB_DIMENSION_T:
1351 case PCB_TEXTBOX_T:
1352 case PCB_BARCODE_T:
1353 return GRID_GRAPHICS;
1354
1355 case PCB_TRACE_T:
1356 case PCB_ARC_T:
1357 return GRID_WIRES;
1358
1359 case PCB_VIA_T:
1360 return GRID_VIAS;
1361
1362 default:
1363 return GRID_CURRENT;
1364 }
1365}
1366
1367
1369{
1370 const GRID_SETTINGS& grid = m_toolMgr->GetSettings()->m_Window.grid;
1371 int idx = -1;
1372
1373 VECTOR2D g = m_toolMgr->GetView()->GetGAL()->GetGridSize();
1374
1375 if( !grid.overrides_enabled )
1376 return g;
1377
1378 switch( aGrid )
1379 {
1380 case GRID_CONNECTABLE:
1381 if( grid.override_connected )
1382 idx = grid.override_connected_idx;
1383
1384 break;
1385
1386 case GRID_WIRES:
1387 if( grid.override_wires )
1388 idx = grid.override_wires_idx;
1389
1390 break;
1391
1392 case GRID_VIAS:
1393 if( grid.override_vias )
1394 idx = grid.override_vias_idx;
1395
1396 break;
1397
1398 case GRID_TEXT:
1399 if( grid.override_text )
1400 idx = grid.override_text_idx;
1401
1402 break;
1403
1404 case GRID_GRAPHICS:
1405 if( grid.override_graphics )
1406 idx = grid.override_graphics_idx;
1407
1408 break;
1409
1410 default:
1411 break;
1412 }
1413
1414 if( idx >= 0 && idx < (int) grid.grids.size() )
1415 g = grid.grids[idx].ToDouble( pcbIUScale );
1416
1417 return g;
1418}
1419
1420
1421std::vector<BOARD_ITEM*> PCB_GRID_HELPER::queryVisible( std::initializer_list<BOX2I> aAreas,
1422 const std::vector<BOARD_ITEM*>& aSkip ) const
1423{
1424 std::vector<BOARD_ITEM*> items;
1425 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> visibleItems;
1426
1427 const bool inFootprintEditor = editingInsideFootprint();
1428 KIGFX::VIEW* view = m_toolMgr->GetView();
1429 RENDER_SETTINGS* settings = view->GetPainter()->GetSettings();
1430 const std::set<int>& activeLayers = settings->GetHighContrastLayers();
1431 bool isHighContrast = settings->GetHighContrast();
1432
1433 view->SyncLayerVisibilityCache(); // Required for ViewGetLOD() calls.
1434
1435 for( const BOX2I& area : aAreas )
1436 {
1437 if( area.GetWidth() > 0 && area.GetHeight() > 0 )
1438 view->Query( area, visibleItems );
1439 }
1440
1441 for( const auto& [viewItem, layer] : visibleItems )
1442 {
1443 if( !viewItem->IsBOARD_ITEM() )
1444 continue;
1445
1446 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( viewItem );
1447
1448 // DRC markers annotate the board rather than being part of it, and every edit tool
1449 // already refuses to operate on them
1450 if( boardItem->Type() == PCB_MARKER_T )
1451 continue;
1452
1453 if( inFootprintEditor )
1454 {
1455 // If we are in the footprint editor, don't use the footprint itself
1456 if( boardItem->Type() == PCB_FOOTPRINT_T )
1457 continue;
1458 }
1459 else
1460 {
1461 // If we are not in the footprint editor, don't use footprint-editor-private items
1462 if( FOOTPRINT* parentFP = boardItem->GetParentFootprint() )
1463 {
1464 if( IsPcbLayer( layer ) && parentFP->GetPrivateLayers().test( layer ) )
1465 continue;
1466 }
1467 }
1468
1469 // The boardItem must be visible and on an active layer
1470 if( view->IsVisible( boardItem ) && ( !isHighContrast || activeLayers.count( layer ) )
1471 && boardItem->ViewGetLOD( layer, view ) < view->GetScale() )
1472 {
1473 items.push_back( boardItem );
1474 }
1475 }
1476
1477 std::sort( items.begin(), items.end(), std::less<>() );
1478 items.erase( std::unique( items.begin(), items.end() ), items.end() );
1479
1480 std::unordered_set<BOARD_ITEM*> skippedItems;
1481
1482 for( BOARD_ITEM* item : aSkip )
1483 {
1484 if( !item )
1485 continue;
1486
1487 skippedItems.insert( item );
1488 item->RunOnChildren(
1489 [&]( BOARD_ITEM* aChild )
1490 {
1491 skippedItems.insert( aChild );
1492 },
1494 }
1495
1496 std::erase_if( items,
1497 [&]( BOARD_ITEM* aItem )
1498 {
1499 return skippedItems.contains( aItem );
1500 } );
1501
1502 return items;
1503}
1504
1505
1507{
1510
1511 // Clang wants this constructor
1513 Item( aItem ),
1514 Geometry( std::move( aSeg ) )
1515 {
1516 }
1517};
1518
1519
1520void PCB_GRID_HELPER::computeAnchors( const std::vector<BOARD_ITEM*>& aItems, const VECTOR2I& aRefPos, bool aFrom,
1521 const PCB_SELECTION_FILTER_OPTIONS* aSelectionFilter, const LSET* aMatchLayers,
1522 bool aForDrag )
1523{
1524 std::vector<PCB_INTERSECTABLE> intersectables;
1525 intersectables.reserve( aItems.size() );
1526
1527 // These could come from a more granular snap mode filter
1528 // But when looking for drag points, we don't want construction geometry
1529 const bool computeIntersections = !aForDrag;
1530 const bool computePointsOnElements = !aForDrag;
1531 const bool excludeGraphics = aSelectionFilter && !aSelectionFilter->graphics;
1532 const bool excludeTracks = aSelectionFilter && !aSelectionFilter->tracks;
1533
1534 const auto itemIsSnappable =
1535 [&]( const BOARD_ITEM& aItem )
1536 {
1537 // If we are filtering by layers, check if the item matches
1538 if( aMatchLayers )
1539 return m_magneticSettings->allLayers || ( ( *aMatchLayers & aItem.GetLayerSet() ).any() );
1540
1541 return true;
1542 };
1543
1544 const auto processItem =
1545 [&]( BOARD_ITEM& item )
1546 {
1547 // Don't even process the item if it doesn't match the layers
1548 if( !itemIsSnappable( item ) )
1549 return;
1550
1551 // First, add all the key points of the item itself
1552 computeAnchors( &item, aRefPos, aFrom, aSelectionFilter );
1553
1554 // If we are computing intersections, construct the relevant intersectables
1555 // Points on elements also use the intersectables.
1556 if( computeIntersections || computePointsOnElements )
1557 {
1558 std::optional<INTERSECTABLE_GEOM> intersectableGeom;
1559
1560 if( !excludeGraphics
1561 && ( item.Type() == PCB_SHAPE_T || item.Type() == PCB_REFERENCE_IMAGE_T ) )
1562 {
1563 intersectableGeom = BoardItemIntersectable( item );
1564 }
1565 else if( !excludeTracks && ( item.Type() == PCB_TRACE_T || item.Type() == PCB_ARC_T ) )
1566 {
1567 intersectableGeom = BoardItemIntersectable( item );
1568 }
1569
1570 if( intersectableGeom )
1571 intersectables.emplace_back( &item, *intersectableGeom );
1572 }
1573 };
1574
1575 for( BOARD_ITEM* item : aItems )
1576 {
1577 processItem( *item );
1578 }
1579
1580 for( const CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM_BATCH& batch : getSnapManager().GetConstructionItems() )
1581 {
1582 for( const CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM& constructionItem : batch )
1583 {
1584 BOARD_ITEM* involvedItem = static_cast<BOARD_ITEM*>( constructionItem.Item );
1585
1586 for( const CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM::DRAWABLE_ENTRY& drawable : constructionItem.Constructions )
1587 {
1588 std::visit(
1589 [&]( const auto& visited )
1590 {
1591 using ItemType = std::decay_t<decltype( visited )>;
1592
1593 if constexpr( std::is_same_v<ItemType, LINE>
1594 || std::is_same_v<ItemType, CIRCLE>
1595 || std::is_same_v<ItemType, HALF_LINE>
1596 || std::is_same_v<ItemType, SHAPE_ARC> )
1597 {
1598 intersectables.emplace_back( involvedItem, visited );
1599 }
1600 else if constexpr( std::is_same_v<ItemType, VECTOR2I> )
1601 {
1602 // Add any free-floating points as snap points.
1603 addAnchor( visited, SNAPPABLE | CONSTRUCTED, involvedItem, POINT_TYPE::PT_NONE );
1604 }
1605 },
1606 drawable.Drawable );
1607 }
1608 }
1609 }
1610
1611 // Now, add all the intersections between the items
1612 // This is obviously quadratic, so performance may be a concern for large selections
1613 // But, so far up to ~20k comparisons seems not to be an issue with run times in the ms range
1614 // and it's usually only a handful of items.
1615
1616 if( computeIntersections )
1617 {
1618 for( std::size_t ii = 0; ii < intersectables.size(); ++ii )
1619 {
1620 const PCB_INTERSECTABLE& intersectableA = intersectables[ii];
1621
1622 for( std::size_t jj = ii + 1; jj < intersectables.size(); ++jj )
1623 {
1624 const PCB_INTERSECTABLE& intersectableB = intersectables[jj];
1625
1626 // An item and its own extension will often have intersections (as they are on top of each other),
1627 // but they not useful points to snap to
1628 if( intersectableA.Item == intersectableB.Item )
1629 continue;
1630
1631 std::vector<VECTOR2I> intersections;
1632 const INTERSECTION_VISITOR visitor{ intersectableA.Geometry, intersections };
1633
1634 std::visit( visitor, intersectableB.Geometry );
1635
1636 // For each intersection, add an intersection snap anchor
1637 for( const VECTOR2I& intersection : intersections )
1638 {
1639 std::vector<EDA_ITEM*> items = {
1640 intersectableA.Item,
1641 intersectableB.Item,
1642 };
1643 addAnchor( intersection, SNAPPABLE | CONSTRUCTED, std::move( items ),
1645 }
1646 }
1647 }
1648 }
1649
1650 // The intersectables can also be used for fall-back snapping to "point on line"
1651 // snaps if no other snap is found
1653
1654 if( computePointsOnElements )
1655 {
1656 // For the moment, it's trivial to make a NEARABLE from an INTERSECTABLE,
1657 // because all INTERSECTABLEs are also NEARABLEs.
1658 for( const PCB_INTERSECTABLE& intersectable : intersectables )
1659 {
1660 std::visit(
1661 [&]( const auto& geom )
1662 {
1663 NEARABLE_GEOM nearable( geom );
1664 m_pointOnLineCandidates.emplace_back( nearable );
1665 },
1666 intersectable.Geometry );
1667 }
1668 }
1669}
1670
1671
1672// Padstacks report a set of "unique" layers, which may each represent one or more
1673// "real" layers. This function takes a unique layer and checks if it applies to the
1674// given "real" layer.
1675static bool PadstackUniqueLayerAppliesToLayer( const PADSTACK& aPadStack, PCB_LAYER_ID aPadstackUniqueLayer,
1676 const PCB_LAYER_ID aRealLayer )
1677{
1678 switch( aPadStack.Mode() )
1679 {
1681 {
1682 // Normal mode padstacks are the same on every layer, so they'll apply to any
1683 // "real" copper layer.
1684 return IsCopperLayer( aRealLayer );
1685 }
1687 {
1688 switch( aPadstackUniqueLayer )
1689 {
1690 case F_Cu:
1691 case B_Cu:
1692 // The outer-layer uhique layers only apply to those exact "real" layers
1693 return aPadstackUniqueLayer == aRealLayer;
1695 // But the inner layers apply to any inner layer
1696 return IsInnerCopperLayer( aRealLayer );
1697 default:
1698 wxFAIL_MSG( wxString::Format( "Unexpected padstack unique layer %d in FRONT_INNER_BACK mode",
1699 aPadstackUniqueLayer ) );
1700 break;
1701 }
1702 break;
1703 }
1705 {
1706 // Custom modes are unique per layer, so it's 1:1
1707 return aRealLayer == aPadstackUniqueLayer;
1708 }
1709 }
1710
1711 return false;
1712};
1713
1714
1715std::vector<PCB_GRID_HELPER::ANCHOR_SPEC> PCB_GRID_HELPER::GetArcAnchors( const PCB_ARC& aArc,
1716 bool aFrom )
1717{
1718 std::vector<ANCHOR_SPEC> anchors;
1719
1720 // The stored midpoint is grid-aligned when the arc is; expose it alongside the endpoints so
1721 // BestDragOrigin picks a grid-aligned corner as the drag/paste reference.
1722 anchors.push_back( { aArc.GetMid(), CORNER | SNAPPABLE, POINT_TYPE::PT_MID } );
1723
1724 // The derived geometric center is rarely grid-aligned. It stays available as a drag origin for
1725 // other items (aFrom=false) but is never offered as this arc's own origin, which was the cause
1726 // of pasted arcs landing off grid.
1727 if( !aFrom )
1728 anchors.push_back( { aArc.GetCenter(), ORIGIN, POINT_TYPE::PT_CENTER } );
1729
1730 return anchors;
1731}
1732
1733
1734void PCB_GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos, bool aFrom,
1735 const PCB_SELECTION_FILTER_OPTIONS* aSelectionFilter )
1736{
1737 KIGFX::VIEW* view = m_toolMgr->GetView();
1738 RENDER_SETTINGS* settings = view->GetPainter()->GetSettings();
1739 const std::set<int>& activeLayers = settings->GetHighContrastLayers();
1740 const PCB_LAYER_ID activeHighContrastPrimaryLayer = settings->GetPrimaryHighContrastLayer();
1741 bool isHighContrast = settings->GetHighContrast();
1742
1743 view->SyncLayerVisibilityCache(); // Required for ViewGetLOD() calls.
1744
1745 const auto checkVisibility =
1746 [&]( const BOARD_ITEM* item )
1747 {
1748 // New moved items don't yet have view flags so VIEW will call them invisible
1749 if( !view->IsVisible( item ) && !item->IsMoving() )
1750 return false;
1751
1752 bool onActiveLayer = !isHighContrast;
1753 bool isLODVisible = false;
1754
1755 for( PCB_LAYER_ID layer : item->GetLayerSet() )
1756 {
1757 if( !onActiveLayer && activeLayers.count( layer ) )
1758 onActiveLayer = true;
1759
1760 if( !isLODVisible && item->ViewGetLOD( layer, view ) < view->GetScale() )
1761 isLODVisible = true;
1762
1763 if( onActiveLayer && isLODVisible )
1764 return true;
1765 }
1766
1767 return false;
1768 };
1769
1770 // As defaults, these are probably reasonable to avoid spamming key points
1771 const KIGEOM::OVAL_KEY_POINT_FLAGS ovalKeyPointFlags = KIGEOM::OVAL_CENTER
1775
1776 auto handlePadShape =
1777 [&]( PAD* aPad, PCB_LAYER_ID aLayer )
1778 {
1780
1782 if( aFrom )
1783 return;
1784
1785 switch( aPad->GetShape( aLayer ) )
1786 {
1787 case PAD_SHAPE::CIRCLE:
1788 {
1789 const CIRCLE circle( aPad->ShapePos( aLayer ), aPad->GetSizeX() / 2 );
1790
1791 for( const TYPED_POINT2I& pt : KIGEOM::GetCircleKeyPoints( circle, false ) )
1792 addAnchor( pt.m_point, OUTLINE | SNAPPABLE, aPad, pt.m_types );
1793
1794 break;
1795 }
1796 case PAD_SHAPE::OVAL:
1797 {
1799 aPad->GetSize( aLayer ), aPad->GetPosition(), aPad->GetOrientation() );
1800
1801 for( const TYPED_POINT2I& pt : KIGEOM::GetOvalKeyPoints( oval, ovalKeyPointFlags ) )
1802 addAnchor( pt.m_point, OUTLINE | SNAPPABLE, aPad, pt.m_types );
1803
1804 break;
1805 }
1810 {
1811 VECTOR2I half_size( aPad->GetSize( aLayer ) / 2 );
1812 VECTOR2I trap_delta( 0, 0 );
1813
1814 if( aPad->GetShape( aLayer ) == PAD_SHAPE::TRAPEZOID )
1815 trap_delta = aPad->GetDelta( aLayer ) / 2;
1816
1817 SHAPE_LINE_CHAIN corners;
1818
1819 corners.Append( -half_size.x - trap_delta.y, half_size.y + trap_delta.x );
1820 corners.Append( half_size.x + trap_delta.y, half_size.y - trap_delta.x );
1821 corners.Append( half_size.x - trap_delta.y, -half_size.y + trap_delta.x );
1822 corners.Append( -half_size.x + trap_delta.y, -half_size.y - trap_delta.x );
1823 corners.SetClosed( true );
1824
1825 corners.Rotate( aPad->GetOrientation() );
1826 corners.Move( aPad->ShapePos( aLayer ) );
1827
1828 for( std::size_t ii = 0; ii < corners.GetSegmentCount(); ++ii )
1829 {
1830 const SEG& seg = corners.GetSegment( ii );
1833
1834 if( ii == corners.GetSegmentCount() - 1 )
1836 }
1837
1838 break;
1839 }
1840
1841 default:
1842 {
1843 const auto& outline = aPad->GetEffectivePolygon( aLayer, ERROR_INSIDE );
1844
1845 if( !outline->IsEmpty() )
1846 {
1847 for( const VECTOR2I& pt : outline->Outline( 0 ).CPoints() )
1848 addAnchor( pt, OUTLINE | SNAPPABLE, aPad );
1849 }
1850
1851 break;
1852 }
1853 }
1854
1855 if( aPad->HasHole() )
1856 {
1857 // Holes are at the pad centre (it's the shape that may be offset)
1858 const VECTOR2I hole_pos = aPad->GetPosition();
1859 const VECTOR2I hole_size = aPad->GetDrillSize();
1860
1861 std::vector<TYPED_POINT2I> snap_pts;
1862
1863 if( hole_size.x == hole_size.y )
1864 {
1865 // Circle
1866 const CIRCLE circle( hole_pos, hole_size.x / 2 );
1867 snap_pts = KIGEOM::GetCircleKeyPoints( circle, true );
1868 }
1869 else
1870 {
1871 // Oval
1872
1873 // For now there's no way to have an off-angle hole, so this is the
1874 // same as the pad. In future, this may not be true:
1875 // https://gitlab.com/kicad/code/kicad/-/issues/4124
1876 const SHAPE_SEGMENT oval =
1877 SHAPE_SEGMENT::BySizeAndCenter( hole_size, hole_pos, aPad->GetOrientation() );
1878 snap_pts = KIGEOM::GetOvalKeyPoints( oval, ovalKeyPointFlags );
1879 }
1880
1881 for( const TYPED_POINT2I& snap_pt : snap_pts )
1882 addAnchor( snap_pt.m_point, OUTLINE | SNAPPABLE, aPad, snap_pt.m_types );
1883 }
1884 };
1885
1886 const auto addRectPoints =
1887 [&]( const BOX2I& aBox, EDA_ITEM& aRelatedItem )
1888 {
1889 const VECTOR2I topRight( aBox.GetRight(), aBox.GetTop() );
1890 const VECTOR2I bottomLeft( aBox.GetLeft(), aBox.GetBottom() );
1891
1892 const SEG first( aBox.GetOrigin(), topRight );
1893 const SEG second( topRight, aBox.GetEnd() );
1894 const SEG third( aBox.GetEnd(), bottomLeft );
1895 const SEG fourth( bottomLeft, aBox.GetOrigin() );
1896
1897 const int snapFlags = CORNER | SNAPPABLE;
1898
1899 addAnchor( aBox.GetCenter(), snapFlags, &aRelatedItem, POINT_TYPE::PT_CENTER );
1900
1901 addAnchor( first.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER );
1902 addAnchor( first.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID );
1903 addAnchor( second.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER );
1904 addAnchor( second.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID );
1905 addAnchor( third.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER );
1906 addAnchor( third.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID );
1907 addAnchor( fourth.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER );
1908 addAnchor( fourth.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID );
1909 };
1910
1911 const auto handleShape =
1912 [&]( PCB_SHAPE* shape )
1913 {
1914 VECTOR2I start = shape->GetStart();
1915 VECTOR2I end = shape->GetEnd();
1916
1917 switch( shape->GetShape() )
1918 {
1919 case SHAPE_T::CIRCLE:
1920 {
1921 const int r = ( start - end ).EuclideanNorm();
1922
1923 addAnchor( start, ORIGIN | SNAPPABLE, shape, POINT_TYPE::PT_CENTER );
1924
1925 addAnchor( start + VECTOR2I( -r, 0 ), OUTLINE | SNAPPABLE, shape, POINT_TYPE::PT_QUADRANT );
1926 addAnchor( start + VECTOR2I( r, 0 ), OUTLINE | SNAPPABLE, shape, POINT_TYPE::PT_QUADRANT );
1927 addAnchor( start + VECTOR2I( 0, -r ), OUTLINE | SNAPPABLE, shape, POINT_TYPE::PT_QUADRANT );
1928 addAnchor( start + VECTOR2I( 0, r ), OUTLINE | SNAPPABLE, shape, POINT_TYPE::PT_QUADRANT );
1929 break;
1930 }
1931
1932 case SHAPE_T::ARC:
1933 addAnchor( shape->GetStart(), CORNER | SNAPPABLE, shape, POINT_TYPE::PT_END );
1934 addAnchor( shape->GetEnd(), CORNER | SNAPPABLE, shape, POINT_TYPE::PT_END );
1935 addAnchor( shape->GetArcMid(), CORNER | SNAPPABLE, shape, POINT_TYPE::PT_MID );
1936 addAnchor( shape->GetCenter(), ORIGIN | SNAPPABLE, shape, POINT_TYPE::PT_CENTER );
1937 break;
1938
1939 case SHAPE_T::RECTANGLE:
1940 {
1941 addRectPoints( BOX2I::ByCorners( start, end ), *shape );
1942 break;
1943 }
1944
1945 case SHAPE_T::SEGMENT:
1946 addAnchor( start, CORNER | SNAPPABLE, shape, POINT_TYPE::PT_END );
1948 addAnchor( shape->GetCenter(), CORNER | SNAPPABLE, shape, POINT_TYPE::PT_MID );
1949 break;
1950
1951 case SHAPE_T::POLY:
1952 {
1954 lc.SetClosed( true );
1955 for( const VECTOR2I& p : shape->GetPolyPoints() )
1956 {
1958 lc.Append( p );
1959 }
1960
1961 addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem );
1962 break;
1963 }
1964
1965 case SHAPE_T::ELLIPSE:
1966 {
1967 VECTOR2I center = shape->GetEllipseCenter();
1968 int majorR = shape->GetEllipseMajorRadius();
1969 int minorR = shape->GetEllipseMinorRadius();
1970 EDA_ANGLE rot = shape->GetEllipseRotation();
1971 VECTOR2I majorEnd( KiROUND( majorR * rot.Cos() ), KiROUND( majorR * rot.Sin() ) );
1972 VECTOR2I minorEnd( KiROUND( -minorR * rot.Sin() ), KiROUND( minorR * rot.Cos() ) );
1973
1975 addAnchor( center + majorEnd, OUTLINE | SNAPPABLE, shape, POINT_TYPE::PT_QUADRANT );
1976 addAnchor( center - majorEnd, OUTLINE | SNAPPABLE, shape, POINT_TYPE::PT_QUADRANT );
1977 addAnchor( center + minorEnd, OUTLINE | SNAPPABLE, shape, POINT_TYPE::PT_QUADRANT );
1978 addAnchor( center - minorEnd, OUTLINE | SNAPPABLE, shape, POINT_TYPE::PT_QUADRANT );
1979 break;
1980 }
1981
1983 {
1984 addAnchor( shape->GetStart(), CORNER | SNAPPABLE, shape, POINT_TYPE::PT_END );
1985 addAnchor( shape->GetEnd(), CORNER | SNAPPABLE, shape, POINT_TYPE::PT_END );
1986 addAnchor( shape->GetEllipseCenter(), ORIGIN | SNAPPABLE, shape, POINT_TYPE::PT_CENTER );
1987 break;
1988 }
1989
1990 case SHAPE_T::BEZIER:
1991 addAnchor( start, CORNER | SNAPPABLE, shape, POINT_TYPE::PT_END );
1994
1995 default:
1996 addAnchor( shape->GetPosition(), ORIGIN | SNAPPABLE, shape );
1997 break;
1998 }
1999 };
2000
2001 switch( aItem->Type() )
2002 {
2003 case PCB_FOOTPRINT_T:
2004 {
2005 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem );
2006 bool footprintVisible = checkVisibility( footprint );
2007
2008 for( PAD* pad : footprint->Pads() )
2009 {
2010 if( aFrom )
2011 {
2012 if( aSelectionFilter && !aSelectionFilter->pads )
2013 continue;
2014 }
2015 else
2016 {
2018 continue;
2019 }
2020
2021 if( !checkVisibility( pad ) )
2022 continue;
2023
2024 if( !pad->GetBoundingBox().Contains( aRefPos ) )
2025 continue;
2026
2027 pad->Padstack().ForEachUniqueLayer(
2028 [&]( PCB_LAYER_ID aLayer )
2029 {
2030 if( !isHighContrast
2031 || PadstackUniqueLayerAppliesToLayer( pad->Padstack(), aLayer,
2032 activeHighContrastPrimaryLayer ) )
2033 {
2034 handlePadShape( pad, aLayer );
2035 }
2036 } );
2037 }
2038
2039 // Points are also pick-up points
2040 for( const PCB_POINT* pt : footprint->Points() )
2041 {
2042 if( aSelectionFilter && !aSelectionFilter->points )
2043 continue;
2044
2045 if( !checkVisibility( pt ) )
2046 continue;
2047
2048 addAnchor( pt->GetPosition(), ORIGIN | SNAPPABLE, footprint, POINT_TYPE::PT_CENTER );
2049 }
2050
2051 // When computing drag origins (aFrom=true), always proceed to add the footprint
2052 // position anchor regardless of the visibility state. The footprint is already
2053 // selected, so its anchor must be reachable as a drag point even if the active layer
2054 // or zoom level causes checkVisibility to return false. Snapping TO an external
2055 // footprint (aFrom=false) should still respect visibility.
2056 if( !footprintVisible && !aFrom )
2057 break;
2058
2059 if( aFrom && aSelectionFilter && !aSelectionFilter->footprints )
2060 break;
2061
2062 // Snap to the footprint origin so that move operations keep the part aligned to
2063 // the grid regardless of anchor layer visibility, but not when the footprint's
2064 // side is hidden.
2065 int fpRenderLayer = ( footprint->GetLayer() == F_Cu ) ? LAYER_FOOTPRINTS_FR
2066 : ( footprint->GetLayer() == B_Cu ) ? LAYER_FOOTPRINTS_BK
2067 : LAYER_ANCHOR;
2068
2069 if( !view->IsLayerVisible( fpRenderLayer ) )
2070 break;
2071
2072 VECTOR2I position = footprint->GetPosition();
2073 VECTOR2I center = footprint->GetBoundingBox( false ).Centre();
2074 VECTOR2I grid( GetGrid() );
2075
2076 addAnchor( position, ORIGIN | SNAPPABLE, footprint, POINT_TYPE::PT_CENTER );
2077
2078 if( ( center - position ).SquaredEuclideanNorm() > grid.SquaredEuclideanNorm() )
2080
2081 break;
2082 }
2083
2084 case PCB_PAD_T:
2085 if( aFrom )
2086 {
2087 if( aSelectionFilter && !aSelectionFilter->pads )
2088 break;
2089 }
2090 else
2091 {
2093 break;
2094 }
2095
2096 if( checkVisibility( aItem ) )
2097 {
2098 PAD* pad = static_cast<PAD*>( aItem );
2099
2100 pad->Padstack().ForEachUniqueLayer(
2101 [&]( PCB_LAYER_ID aLayer )
2102 {
2103 if( !isHighContrast
2104 || PadstackUniqueLayerAppliesToLayer( pad->Padstack(), aLayer,
2105 activeHighContrastPrimaryLayer ) )
2106 {
2107 handlePadShape( pad, aLayer );
2108 }
2109 } );
2110 }
2111
2112 break;
2113
2114 case PCB_TEXTBOX_T:
2115 if( aFrom )
2116 {
2117 if( aSelectionFilter && !aSelectionFilter->text )
2118 break;
2119 }
2120 else
2121 {
2122 if( !m_magneticSettings->graphics )
2123 break;
2124 }
2125
2126 if( checkVisibility( aItem ) )
2127 handleShape( static_cast<PCB_SHAPE*>( aItem ) );
2128
2129 break;
2130
2131 case PCB_TABLE_T:
2132 case PCB_DRILL_CHART_T:
2133 if( aFrom )
2134 {
2135 if( aSelectionFilter && !aSelectionFilter->text )
2136 break;
2137 }
2138 else
2139 {
2140 if( !m_magneticSettings->graphics )
2141 break;
2142 }
2143
2144 if( checkVisibility( aItem ) )
2145 {
2146 PCB_TABLE* table = static_cast<PCB_TABLE*>( aItem );
2147
2148 EDA_ANGLE drawAngle = table->GetCell( 0, 0 )->GetDrawRotation();
2149 VECTOR2I topLeft = table->GetCell( 0, 0 )->GetCornersInSequence( drawAngle )[0];
2150 VECTOR2I bottomLeft =
2151 table->GetCell( table->GetRowCount() - 1, 0 )->GetCornersInSequence( drawAngle )[3];
2152 VECTOR2I topRight = table->GetCell( 0, table->GetColCount() - 1 )->GetCornersInSequence( drawAngle )[1];
2153 VECTOR2I bottomRight = table->GetCell( table->GetRowCount() - 1, table->GetColCount() - 1 )
2154 ->GetCornersInSequence( drawAngle )[2];
2155
2160
2161 addAnchor( table->GetCenter(), ORIGIN, table, POINT_TYPE::PT_MID );
2162 }
2163
2164 break;
2165
2166 case PCB_DRILL_MAP_T:
2167 if( aFrom )
2168 {
2169 if( aSelectionFilter && !aSelectionFilter->graphics )
2170 break;
2171 }
2172 else if( !m_magneticSettings->graphics )
2173 {
2174 break;
2175 }
2176
2177 if( checkVisibility( aItem ) )
2178 {
2179 const PCB_DRILL_MAP* map = static_cast<const PCB_DRILL_MAP*>( aItem );
2180 const BOX2I box = map->GetBoundingBox();
2181
2182 // The offset is the only thing a map owns, so snapping it back onto the origin is
2183 // how the marks are put back on their holes
2185
2188 addAnchor( VECTOR2I( box.GetRight(), box.GetTop() ), CORNER | SNAPPABLE, aItem,
2190 addAnchor( VECTOR2I( box.GetLeft(), box.GetBottom() ), CORNER | SNAPPABLE, aItem,
2192 }
2193
2194 break;
2195
2196 case PCB_SHAPE_T:
2197 if( aFrom )
2198 {
2199 if( aSelectionFilter && !aSelectionFilter->graphics )
2200 break;
2201 }
2202 else
2203 {
2204 if( !m_magneticSettings->graphics )
2205 break;
2206 }
2207
2208 if( checkVisibility( aItem ) )
2209 handleShape( static_cast<PCB_SHAPE*>( aItem ) );
2210
2211 break;
2212
2213 case PCB_TRACE_T:
2214 case PCB_ARC_T:
2215 if( aFrom )
2216 {
2217 if( aSelectionFilter && !aSelectionFilter->tracks )
2218 break;
2219 }
2220 else
2221 {
2223 break;
2224 }
2225
2226 if( checkVisibility( aItem ) )
2227 {
2228 PCB_TRACK* track = static_cast<PCB_TRACK*>( aItem );
2229
2230 addAnchor( track->GetStart(), CORNER | SNAPPABLE, track, POINT_TYPE::PT_END );
2231 addAnchor( track->GetEnd(), CORNER | SNAPPABLE, track, POINT_TYPE::PT_END );
2232
2233 if( aItem->Type() == PCB_ARC_T )
2234 {
2235 PCB_ARC* arc = static_cast<PCB_ARC*>( aItem );
2236
2237 for( const ANCHOR_SPEC& spec : GetArcAnchors( *arc, aFrom ) )
2238 addAnchor( spec.pos, spec.flags, arc, spec.pointType );
2239 }
2240 else
2241 {
2242 addAnchor( track->GetCenter(), ORIGIN, track, POINT_TYPE::PT_MID );
2243 }
2244 }
2245
2246 break;
2247
2248 case PCB_TARGET_T:
2250 break;
2251
2252 case PCB_GRID_ITEM_T:
2253 {
2254 // Edit handles only - grid intersections are rendered by the GAL and not
2255 // emitted as anchors (would flood the snap pool).
2256 PCB_GRID_ITEM* griditem = static_cast<PCB_GRID_ITEM*>( aItem );
2257 const VECTOR2I position = griditem->GetPosition();
2258 const EDA_ANGLE orient = griditem->GetOrientation();
2259
2260 addAnchor( position, ORIGIN | CORNER | SNAPPABLE, griditem, POINT_TYPE::PT_CENTER );
2261
2262 const auto pushHandle = [&]( VECTOR2I aLocal )
2263 {
2264 RotatePoint( aLocal, orient );
2265 addAnchor( position + aLocal, CORNER | SNAPPABLE, griditem );
2266 };
2267
2268 if( griditem->GetGridItemType() == PCB_GRID_TYPE::POLAR )
2269 {
2270 const int r = griditem->GetRadiusExtent();
2271 const double phi = griditem->GetPhiExtent().AsRadians();
2272 pushHandle( VECTOR2I( r, 0 ) );
2273 pushHandle( VECTOR2I( KiROUND( r * std::cos( phi ) ), KiROUND( r * std::sin( phi ) ) ) );
2274 pushHandle( VECTOR2I( KiROUND( r * std::cos( phi / 2.0 ) ), KiROUND( r * std::sin( phi / 2.0 ) ) ) );
2275 }
2276 else
2277 {
2278 const VECTOR2I e = griditem->GetExtent();
2279 pushHandle( VECTOR2I( -e.x, -e.y ) );
2280 pushHandle( VECTOR2I( e.x, -e.y ) );
2281 pushHandle( VECTOR2I( e.x, e.y ) );
2282 pushHandle( VECTOR2I( -e.x, e.y ) );
2283 }
2284 break;
2285 }
2286
2287 case PCB_POINT_T:
2288 if( aSelectionFilter && !aSelectionFilter->points )
2289 break;
2290
2291 if( checkVisibility( aItem ) )
2293
2294 break;
2295
2296 case PCB_VIA_T:
2297 if( aFrom )
2298 {
2299 if( aSelectionFilter && !aSelectionFilter->vias )
2300 break;
2301 }
2302 else
2303 {
2305 break;
2306 }
2307
2308 if( checkVisibility( aItem ) )
2310
2311 break;
2312
2313 case PCB_ZONE_T:
2314 if( aFrom && aSelectionFilter && !aSelectionFilter->zones )
2315 break;
2316
2317 if( checkVisibility( aItem ) )
2318 {
2319 const SHAPE_POLY_SET* outline = static_cast<const ZONE*>( aItem )->Outline();
2320
2322 lc.SetClosed( true );
2323
2324 for( auto iter = outline->CIterateWithHoles(); iter; iter++ )
2325 {
2326 addAnchor( *iter, CORNER | SNAPPABLE, aItem, POINT_TYPE::PT_CORNER );
2327 lc.Append( *iter );
2328 }
2329
2330 addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem );
2331 }
2332
2333 break;
2334
2335 case PCB_DIM_ALIGNED_T:
2337 if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
2338 break;
2339
2340 if( checkVisibility( aItem ) )
2341 {
2342 PCB_DIM_ALIGNED* dim = static_cast<PCB_DIM_ALIGNED*>( aItem );
2343 addAnchor( dim->GetCrossbarStart(), CORNER | SNAPPABLE, dim );
2344 addAnchor( dim->GetCrossbarEnd(), CORNER | SNAPPABLE, dim );
2345 addAnchor( dim->GetStart(), CORNER | SNAPPABLE, dim );
2346 addAnchor( dim->GetEnd(), CORNER | SNAPPABLE, dim );
2347 }
2348
2349 break;
2350
2351 case PCB_DIM_CENTER_T:
2352 if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
2353 break;
2354
2355 if( checkVisibility( aItem ) )
2356 {
2357 PCB_DIM_CENTER* dim = static_cast<PCB_DIM_CENTER*>( aItem );
2358 addAnchor( dim->GetStart(), CORNER | SNAPPABLE, dim );
2359 addAnchor( dim->GetEnd(), CORNER | SNAPPABLE, dim );
2360
2361 VECTOR2I start( dim->GetStart() );
2362 VECTOR2I radial( dim->GetEnd() - dim->GetStart() );
2363
2364 for( int i = 0; i < 2; i++ )
2365 {
2366 RotatePoint( radial, -ANGLE_90 );
2367 addAnchor( start + radial, CORNER | SNAPPABLE, dim );
2368 }
2369 }
2370
2371 break;
2372
2373 case PCB_DIM_RADIAL_T:
2374 if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
2375 break;
2376
2377 if( checkVisibility( aItem ) )
2378 {
2379 PCB_DIM_RADIAL* radialDim = static_cast<PCB_DIM_RADIAL*>( aItem );
2380 addAnchor( radialDim->GetStart(), CORNER | SNAPPABLE, radialDim );
2381 addAnchor( radialDim->GetEnd(), CORNER | SNAPPABLE, radialDim );
2382 addAnchor( radialDim->GetKnee(), CORNER | SNAPPABLE, radialDim );
2383 addAnchor( radialDim->GetTextPos(), CORNER | SNAPPABLE, radialDim );
2384 }
2385
2386 break;
2387
2388 case PCB_DIM_LEADER_T:
2389 if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
2390 break;
2391
2392 if( checkVisibility( aItem ) )
2393 {
2394 PCB_DIM_LEADER* leader = static_cast<PCB_DIM_LEADER*>( aItem );
2395 addAnchor( leader->GetStart(), CORNER | SNAPPABLE, leader );
2396 addAnchor( leader->GetEnd(), CORNER | SNAPPABLE, leader );
2397 addAnchor( leader->GetTextPos(), CORNER | SNAPPABLE, leader );
2398 }
2399
2400 break;
2401
2402 case PCB_FIELD_T:
2403 case PCB_TEXT_T:
2404 if( aFrom && aSelectionFilter && !aSelectionFilter->text )
2405 break;
2406
2407 if( checkVisibility( aItem ) )
2408 addAnchor( aItem->GetPosition(), ORIGIN, aItem );
2409
2410 break;
2411
2412 case PCB_BARCODE_T:
2413 if( aFrom && aSelectionFilter && !aSelectionFilter->otherItems )
2414 break;
2415
2416 if( checkVisibility( aItem ) )
2417 {
2418 PCB_BARCODE* barcode = static_cast<PCB_BARCODE*>( aItem );
2419 const BOX2I bbox = barcode->GetSymbolPoly().BBox();
2420
2421 addAnchor( aItem->GetPosition(), ORIGIN, barcode, POINT_TYPE::PT_CENTER );
2422 addRectPoints( bbox, *barcode );
2423 }
2424
2425 break;
2426
2427 case PCB_GROUP_T:
2428 for( BOARD_ITEM* item : static_cast<PCB_GROUP*>( aItem )->GetBoardItems() )
2429 {
2430 if( checkVisibility( item ) )
2431 computeAnchors( item, aRefPos, aFrom, nullptr );
2432 }
2433
2434 break;
2435
2437 if( aFrom && aSelectionFilter && !aSelectionFilter->graphics )
2438 break;
2439
2440 if( checkVisibility( aItem ) )
2441 {
2442 PCB_REFERENCE_IMAGE* image = static_cast<PCB_REFERENCE_IMAGE*>( aItem );
2443 const REFERENCE_IMAGE& refImg = image->GetReferenceImage();
2444 const BOX2I bbox = refImg.GetBoundingBox();
2445
2446 addRectPoints( bbox, *image );
2447
2448 if( refImg.GetTransformOriginOffset() != VECTOR2I( 0, 0 ) )
2449 {
2450 addAnchor( image->GetPosition() + refImg.GetTransformOriginOffset(), ORIGIN,
2452 }
2453 }
2454
2455 break;
2456
2457 default:
2458 break;
2459 }
2460}
2461
2462
2464{
2465 // Do this all in squared distances as we only care about relative distances
2467
2468 ecoord minDist = std::numeric_limits<ecoord>::max();
2469 std::vector<ANCHOR*> anchorsAtMinDistance;
2470
2471 for( ANCHOR& anchor : m_anchors )
2472 {
2473 // There is no need to filter by layers here, as the items are already filtered
2474 // by layer (if needed) when the anchors are computed.
2475 if( ( aFlags & anchor.flags ) != aFlags )
2476 continue;
2477
2478 if( !anchorsAtMinDistance.empty() && anchor.pos == anchorsAtMinDistance.front()->pos )
2479 {
2480 // Same distance as the previous best anchor
2481 anchorsAtMinDistance.push_back( &anchor );
2482 }
2483 else
2484 {
2485 const double dist = anchor.pos.SquaredDistance( aPos );
2486
2487 if( dist < minDist )
2488 {
2489 // New minimum distance
2490 minDist = dist;
2491 anchorsAtMinDistance.clear();
2492 anchorsAtMinDistance.push_back( &anchor );
2493 }
2494 }
2495 }
2496
2497 // Check that any involved real items are 'active'
2498 // (i.e. the user has moused over a key point previously)
2499 // If any are not real (e.g. snap lines), they are allowed to be involved
2500 //
2501 // This is an area most likely to be controversial/need tuning,
2502 // as some users will think it's fiddly; without 'activation', others will
2503 // think the snaps are intrusive.
2504 SNAP_MANAGER& snapManager = getSnapManager();
2505
2506 auto noRealItemsInAnchorAreInvolved =
2507 [&]( ANCHOR* aAnchor ) -> bool
2508 {
2509 // If no extension snaps are enabled, don't inhibit
2510 static const bool haveExtensions = ADVANCED_CFG::GetCfg().m_EnableExtensionSnaps;
2511
2512 if( !haveExtensions )
2513 return false;
2514
2515 // If the anchor is not constructed, it may be involved (because it is one
2516 // of the nearest anchors). The items will only be activated later, but don't
2517 // discard the anchor yet.
2518 const bool anchorIsConstructed = aAnchor->flags & ANCHOR_FLAGS::CONSTRUCTED;
2519
2520 if( !anchorIsConstructed )
2521 return false;
2522
2523 bool allRealAreInvolved = snapManager.GetConstructionManager().InvolvesAllGivenRealItems( aAnchor->items );
2524 return !allRealAreInvolved;
2525 };
2526
2527 // Trim out items that aren't involved
2528 std::erase_if( anchorsAtMinDistance, noRealItemsInAnchorAreInvolved );
2529
2530 // More than one anchor can be at the same distance, for example
2531 // two lines end-to-end each have the same endpoint anchor.
2532 // So, check which one has an involved item that's closest to the origin,
2533 // and use that one (which allows the user to choose which items
2534 // gets extended - it's the one nearest the cursor)
2535 ecoord minDistToItem = std::numeric_limits<ecoord>::max();
2536 ANCHOR* best = nullptr;
2537
2538 // One of the anchors at the minimum distance
2539 for( ANCHOR* const anchor : anchorsAtMinDistance )
2540 {
2541 ecoord distToNearestItem = std::numeric_limits<ecoord>::max();
2542
2543 for( EDA_ITEM* const item : anchor->items )
2544 {
2545 if( !item || !item->IsBOARD_ITEM() )
2546 continue;
2547
2548 std::optional<ecoord> distToThisItem =
2549 FindSquareDistanceToItem( static_cast<const BOARD_ITEM&>( *item ), aPos );
2550
2551 if( distToThisItem )
2552 distToNearestItem = std::min( distToNearestItem, *distToThisItem );
2553 }
2554
2555 // If the item doesn't have any special min-dist handler,
2556 // just use the distance to the anchor
2557 distToNearestItem = std::min( distToNearestItem, minDist );
2558
2559 if( distToNearestItem < minDistToItem )
2560 {
2561 minDistToItem = distToNearestItem;
2562 best = anchor;
2563 }
2564 }
2565
2566 return best;
2567}
@ ERROR_INSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
std::optional< INTERSECTABLE_GEOM > BoardItemIntersectable(const BOARD_ITEM &aItem)
The kimath primitive a board item is made of.
constexpr BOX2I BOX2ISafe(const BOX2D &aInput)
Definition box2.h:921
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
virtual VECTOR2I GetCenter() const
This defaults to the center of the bounding box if not overridden.
Definition board_item.h:150
FOOTPRINT * GetParentFootprint() const
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:346
virtual void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const
Invoke a function on all children.
Definition board_item.h:264
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:266
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
bool IsFootprintHolder() const
Find out if the board is being used to hold a single footprint for editing/viewing.
Definition board.h:440
bool IsElementVisible(GAL_LAYER_ID aLayer) const
Test whether a given element category is visible.
Definition board.cpp:1252
void AddListener(BOARD_LISTENER *aListener)
Add a listener to the board to receive calls whenever something on the board has been modified.
Definition board.cpp:3995
void RemoveListener(BOARD_LISTENER *aListener)
Remove the specified listener.
Definition board.cpp:4002
const DRAWINGS & Drawings() const
Definition board.h:466
constexpr BOX2< Vec > Intersect(const BOX2< Vec > &aRect)
Definition box2.h:344
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:553
static constexpr BOX2< VECTOR2I > ByCorners(const VECTOR2I &aCorner1, const VECTOR2I &aCorner2)
Definition box2.h:67
constexpr const Vec GetEnd() const
Definition box2.h:209
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr Vec Centre() const
Definition box2.h:94
constexpr const Vec GetCenter() const
Definition box2.h:227
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr coord_type GetLeft() const
Definition box2.h:225
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:165
constexpr const Vec & GetOrigin() const
Definition box2.h:207
constexpr coord_type GetRight() const
Definition box2.h:214
constexpr coord_type GetTop() const
Definition box2.h:226
constexpr void Offset(coord_type dx, coord_type dy)
Definition box2.h:256
constexpr coord_type GetBottom() const
Definition box2.h:219
Represent basic circle geometry with utility geometry functions.
Definition circle.h:33
void ProposeConstructionItems(std::unique_ptr< CONSTRUCTION_ITEM_BATCH > aBatch, bool aIsPersistent)
Add a batch of construction items to the helper.
void CancelProposal()
Cancel outstanding proposals for new geometry.
void Clear()
Clear all construction items.
std::vector< CONSTRUCTION_ITEM > CONSTRUCTION_ITEM_BATCH
bool InvolvesAllGivenRealItems(const std::vector< EDA_ITEM * > &aItems) const
Check if all 'real' (non-null = constructed) the items in the batch are in the list of items currentl...
double Sin() const
Definition eda_angle.h:177
double AsRadians() const
Definition eda_angle.h:119
double Cos() const
Definition eda_angle.h:196
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
virtual VECTOR2I GetPosition() const
Definition eda_item.h:348
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:270
const KIID m_Uuid
Definition eda_item.h:599
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
virtual bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const
Test if aPosition is inside or on the boundary of this item.
Definition eda_item.h:309
bool IsMoving() const
Definition eda_item.h:132
const VECTOR2I & GetEllipseCenter() const
Definition eda_shape.h:377
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:175
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:325
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:275
VECTOR2I GetArcMid() const
PCB_POINTS & Points()
Definition footprint.h:420
std::deque< PAD * > & Pads()
Definition footprint.h:405
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition footprint.h:450
VECTOR2I GetPosition() const override
Definition footprint.h:436
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
void retainAcceptedSnaps(const SNAP_RESULT &aResult)
Record which snaps the resolver accepted so they can be re-biased on the next resolve,...
std::optional< VECTOR2I > SnapToConstructionLines(const VECTOR2I &aPoint, const VECTOR2I &aNearestGrid, const VECTOR2D &aGrid, double aSnapRange) const
void addAnchor(const VECTOR2I &aPos, int aFlags, EDA_ITEM *aItem, int aPointTypes=POINT_TYPE::PT_NONE)
SNAP_MANAGER & getSnapManager()
VECTOR2I m_skipPoint
bool m_enableGrid
SNAP_RANGES computeSnapRanges(bool aClampToVisibleGrid) const
Compute the snap thresholds.
void emitAngleBranchCandidates(std::vector< SNAP_CANDIDATE > &aCandidates, const VECTOR2I &aOrigin, double aSnapScale) const
Emit the angle-restriction snap candidates (the two branches bracketing the cursor angle) into the cu...
void showConstructionGeometry(bool aShow)
SNAP_RESOLVER::FEASIBILITY_CALLBACK m_feasibilityCallback
std::vector< SEG > m_stationarySelfSegments
TOOL_MANAGER * m_toolMgr
VECTOR2D GetVisibleGrid() const
SNAP_RESOLVER::TRACE_CALLBACK snapTraceCallback(const SNAP_SOURCE_CONTEXT &aContext) const
VECTOR2I GetGrid() const
bool m_enableSnapLine
bool m_enableSnap
VECTOR2I GetOrigin() const
bool canUseGrid() const
Check whether it is possible to use the grid – this depends both on local grid helper settings and gl...
std::optional< SNAP_STABLE_ID > m_retainedAngleBranch
void clearAnchors()
std::optional< ANCHOR > m_snapItem
SNAP_REFERENCE_PREFERENCE m_layoutReferencePreference
KIGFX::ANCHOR_DEBUG * enableAndGetAnchorDebug()
Enable the anchor debug if permitted and return it.
KIGFX::SNAP_INDICATOR m_viewSnapPoint
virtual VECTOR2I Align(const VECTOR2I &aPoint, GRID_HELPER_GRIDS aGrid) const
void updateSnapPoint(const TYPED_POINT2I &aPoint)
KIGFX::ORIGIN_VIEWITEM m_viewAxis
bool m_pointEditProfile
std::vector< SNAP_STABLE_ID > m_stickySnapIds
void setLayoutReference(const VECTOR2I &aPoint, const std::optional< BOX2I > &aBounds, bool aAnchorPoint)
Record the classified drag reference and trace it.
void emitSelfAndGridCandidates(std::vector< SNAP_CANDIDATE > &aCandidates, const SNAP_SOURCE_CONTEXT &aContext, const VECTOR2I &aOrigin, const VECTOR2I &aNearestGrid, double aSnapScale, int aSnapRange, bool aUseGrid) const
Emit the point editor's unchanged self-geometry and the independent grid axes.
void applySnapResultGuides(const SNAP_RESULT &aResult)
std::vector< ANCHOR > m_anchors
View item to draw debug items for anchors.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
COLOR4D WithAlpha(double aAlpha) const
Return a color with the same color, but the given alpha.
Definition color4d.h:309
COLOR4D Brightened(double aFactor) const
Return a color that is brighter by a given factor, without modifying object.
Definition color4d.h:265
virtual RENDER_SETTINGS * GetSettings()=0
Return a pointer to current settings that are going to be used when drawing items.
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
const std::set< int > GetHighContrastLayers() const
Returns the set of currently high-contrast layers.
PCB_LAYER_ID GetPrimaryHighContrastLayer() const
Return the board layer which is in high-contrast mode.
const COLOR4D & GetLayerColor(int aLayer) const
Return the color used to draw a layer.
bool IsBOARD_ITEM() const
Definition view_item.h:98
virtual double ViewGetLOD(int aLayer, const VIEW *aView) const
Return the level of detail (LOD) of the item.
Definition view_item.h:151
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:63
double GetScale() const
Definition view.h:283
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition view.cpp:297
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition view.cpp:412
int Query(const BOX2I &aRect, std::vector< LAYER_ITEM_PAIR > &aResult) const
Find all visible items that touch or are within the rectangle aRect.
Definition view.cpp:501
void SyncLayerVisibilityCache()
Definition view.cpp:1957
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition view.h:429
PAINTER * GetPainter() const
Return the painter object used by the view for drawing VIEW_ITEM objects.
Definition view.h:227
bool IsVisible(const VIEW_ITEM *aItem) const
Return information if the item is visible (or not).
Definition view.cpp:1862
void SetVisible(VIEW_ITEM *aItem, bool aIsVisible=true)
Set the item visibility.
Definition view.cpp:1813
Definition line.h:32
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllLayersMask()
Definition lset.cpp:637
A PADSTACK defines the characteristics of a single or multi-layer pad, in the IPC sense of the word.
Definition padstack.h:156
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:170
@ CUSTOM
Shapes can be defined on arbitrary layers.
Definition padstack.h:172
@ FRONT_INNER_BACK
Up to three shapes can be defined (F_Cu, inner copper layers, B_Cu)
Definition padstack.h:171
MODE Mode() const
Definition padstack.h:344
static constexpr PCB_LAYER_ID INNER_LAYERS
! The layer identifier to use for "inner layers" on top/inner/bottom padstacks
Definition padstack.h:182
Definition pad.h:61
int GetSizeX() const
Definition pad.cpp:322
const VECTOR2I & GetDelta(PCB_LAYER_ID aLayer) const
Definition pad.h:307
VECTOR2I GetPosition() const override
Definition pad.cpp:256
VECTOR2I GetDrillSize() const
Definition pad.h:320
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:205
VECTOR2I GetSize(PCB_LAYER_ID aLayer) const
Definition pad.cpp:298
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition pad.cpp:1757
const std::shared_ptr< SHAPE_POLY_SET > & GetEffectivePolygon(PCB_LAYER_ID aLayer, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Definition pad.cpp:1240
bool HasHole() const override
Definition pad.h:113
VECTOR2I ShapePos(PCB_LAYER_ID aLayer) const
Definition pad.cpp:1868
const VECTOR2I & GetMid() const
Definition pcb_track.h:295
virtual VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_track.h:302
const SHAPE_POLY_SET & GetSymbolPoly() const
Access the cached polygon for the barcode symbol only (no text, no margins/knockout).
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
PCB_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
virtual VECTOR2I GetEnd() const
virtual VECTOR2I GetStart() const
The dimension's origin is the first feature point for the dimension.
For better understanding of the points that make a dimension:
const VECTOR2I & GetCrossbarStart() const
const VECTOR2I & GetCrossbarEnd() const
Mark the center of a circle or arc with a cross shape.
A leader is a dimension-like object pointing to a specific point.
A radial dimension indicates either the radius or diameter of an arc or circle.
VECTOR2I GetKnee() const
Turns on drill symbols at the holes, for one layer.
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
const VECTOR2I & GetOffset() const
std::vector< NEARABLE_GEOM > m_pointOnLineCandidates
void OnBoardItemRemoved(BOARD &aBoard, BOARD_ITEM *aRemovedItem) override
std::vector< BOARD_ITEM * > queryVisible(std::initializer_list< BOX2I > aAreas, const std::vector< BOARD_ITEM * > &aSkip) const
static std::vector< ANCHOR_SPEC > GetArcAnchors(const PCB_ARC &aArc, bool aFrom)
Return the snap/drag anchor points that a track arc contributes.
SNAP_RESULT ResolveSnap(const VECTOR2I &aOrigin, BOARD_ITEM *aReferenceItem, GRID_HELPER_GRIDS aGrid=GRID_HELPER_GRIDS::GRID_CURRENT)
Choose the "best" snap anchor around the given point, optionally taking layers from the reference ite...
SNAP_INFERENCE_SETTINGS snapInferenceSettings() const
Snap inference settings for whichever editor owns this helper.
~PCB_GRID_HELPER() override
VECTOR2I Align(const VECTOR2I &aPoint, GRID_HELPER_GRIDS aGrid) const override
VECTOR2I AlignToArc(const VECTOR2I &aPoint, const SHAPE_ARC &aSeg)
VECTOR2I SnapToPad(const VECTOR2I &aMousePos, std::deque< PAD * > &aPads)
void OnBoardItemsRemoved(BOARD &aBoard, std::vector< BOARD_ITEM * > &aBoardItems) override
BOARD_ITEM * GetSnapped() const
If the PCB_GRID_HELPER has highlighted a snap point (target shown), this function will return a point...
VECTOR2D GetGridSize(GRID_HELPER_GRIDS aGrid) const override
Return the size of the specified grid.
VECTOR2I BestDragOrigin(const VECTOR2I &aMousePos, std::vector< BOARD_ITEM * > &aItem, GRID_HELPER_GRIDS aGrid=GRID_HELPER_GRIDS::GRID_CURRENT, const PCB_SELECTION_FILTER_OPTIONS *aSelectionFilter=nullptr)
bool editingInsideFootprint() const
True when the footprint's own contents are the layout objects.
void AddConstructionItems(std::vector< BOARD_ITEM * > aItems, bool aExtensionOnly, bool aIsPersistent)
Add construction geometry for a set of board items.
ANCHOR * nearestAnchor(const VECTOR2I &aPos, int aFlags)
Find the nearest anchor point to the given position with matching flags.
static BOX2I layoutBounds(const BOARD_ITEM &aItem)
MAGNETIC_SETTINGS * m_magneticSettings
GRID_HELPER_GRIDS GetItemGrid(const EDA_ITEM *aItem) const override
Get the coarsest grid that applies to an item.
VECTOR2I AlignToSegment(const VECTOR2I &aPoint, const SEG &aSeg)
bool m_constructionGeometryEnabled
void computeAnchors(const std::vector< BOARD_ITEM * > &aItems, const VECTOR2I &aRefPos, bool aFrom, const PCB_SELECTION_FILTER_OPTIONS *aSelectionFilter, const LSET *aLayers, bool aForDrag)
computeAnchors inserts the local anchor points in to the grid helper for the specified container of b...
std::set< SNAP_CANDIDATE_SUBTYPE > m_suppressedSnapSubtypes
EDA_ANGLE GetPhiExtent() const
VECTOR2I GetExtent() const
VECTOR2I GetPosition() const override
GRID_GEOMETRY AsGridGeometry() const
Project this grid into a GRID_GEOMETRY (doubles, radians) for shared math.
EDA_ANGLE GetOrientation() const
int GetRadiusExtent() const
PCB_GRID_TYPE GetGridItemType() const
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:51
A PCB_POINT is a 0-dimensional point that is used to mark a position on a PCB, or more usually a foot...
Definition pcb_point.h:39
Object to handle a bitmap image that can be inserted in a PCB.
REFERENCE_IMAGE & GetReferenceImage()
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_shape.h:78
VECTOR2I GetTextPos() const override
Definition pcb_text.cpp:461
const VECTOR2I & GetStart() const
Definition pcb_track.h:98
const VECTOR2I & GetEnd() const
Definition pcb_track.h:95
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
VECTOR2I GetTransformOriginOffset() const
Get the center of scaling, etc, relative to the image center (GetPosition()).
VECTOR2I GetPosition() const
BOX2I GetBoundingBox() const
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
ecoord SquaredDistance(const SEG &aSeg) const
Definition seg.cpp:35
VECTOR2I::extended_type ecoord
Definition seg.h:40
VECTOR2I B
Definition seg.h:46
static SEG::ecoord Square(int a)
Definition seg.h:119
VECTOR2I Center() const
Returns the center point of the line.
Definition seg.h:376
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition seg.h:217
const VECTOR2I & GetP1() const
Definition shape_arc.h:115
int IntersectLine(const SEG &aSeg, std::vector< VECTOR2I > *aIpsBuffer) const
Find intersection points between this arc and aSeg, treating aSeg as an infinite line.
const VECTOR2I & GetP0() const
Definition shape_arc.h:114
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void Move(const VECTOR2I &aVector) override
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
virtual const SEG GetSegment(int aIndex) const override
const VECTOR2I NearestPoint(const VECTOR2I &aP, bool aAllowInternalShapePoints=true) const
Find a point on the line chain that is closest to point aP.
virtual size_t GetSegmentCount() const override
Represent a set of closed polygons.
CONST_ITERATOR CIterateWithHoles(int aOutline) const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
static SHAPE_SEGMENT BySizeAndCenter(const VECTOR2I &aSize, const VECTOR2I &aCenter, const EDA_ANGLE &aRotation)
std::vector< SNAP_CANDIDATE > CollectObjectGeometry(const SNAP_SOURCE_CONTEXT &aContext, int aRadius) const
std::vector< SNAP_CANDIDATE > CollectEqualSpacing(const SNAP_SOURCE_CONTEXT &aContext, int aRadius) const
void AddBounds(SNAP_OBJECT_BOUNDS aBounds)
std::vector< SNAP_CANDIDATE > CollectTangentNormal(const SNAP_SOURCE_CONTEXT &aContext, int aRadius, bool aTangentEnabled, bool aNormalEnabled) const
void AddPath(SNAP_OBJECT_PATH aPath)
void AddAlignmentPoint(SNAP_ALIGNMENT_POINT aPoint)
std::vector< SNAP_CANDIDATE > CollectAlignment(const SNAP_SOURCE_CONTEXT &aContext, int aRadius) const
OPT_VECTOR2I GetNearestSnapLinePoint(const VECTOR2I &aCursor, const VECTOR2I &aNearestGrid, std::optional< int > aDistToNearest, int snapRange, const VECTOR2D &aGridSize=VECTOR2D(0, 0), const VECTOR2I &aGridOrigin=VECTOR2I(0, 0)) const
If the snap line is active, return the best snap point that is closest to the cursor.
void SetSnappedAnchor(const VECTOR2I &aAnchorPos)
Inform this manager that an anchor snap has been made.
void ClearSnapLine()
Clear the snap line origin and end points.
void SetSnapLineOrigin(const VECTOR2I &aOrigin)
The snap point is a special point that is located at the last point the cursor snapped to.
void SetSnapLineEnd(const OPT_VECTOR2I &aSnapPoint)
Set the end point of the snap line.
A SNAP_MANAGER glues together the snap line manager and construction manager., along with some other ...
SNAP_LINE_MANAGER & GetSnapLineManager()
CONSTRUCTION_MANAGER & GetConstructionManager()
const std::vector< VECTOR2I > & GetReferenceOnlyPoints() const
void SetReferenceOnlyPoints(std::vector< VECTOR2I > aPoints)
Set the reference-only points - these are points that are not snapped to, but can still be used for c...
void SetDimensionBrackets(std::vector< SEG > aBrackets)
Master controller class:
EDA_ITEM * GetModel() const
Define a general 2D-vector/point.
Definition vector2d.h:67
constexpr extended_type SquaredDistance(const VECTOR2< T > &aVector) const
Compute the squared distance between two vectors.
Definition vector2d.h:582
static constexpr extended_type ECOORD_MAX
Definition vector2d.h:72
VECTOR2_TRAITS< int32_t >::extended_type extended_type
Definition vector2d.h:69
Handle a list of polygons defining a copper zone.
Definition zone.h:70
A type-safe container of any type.
Definition ki_any.h:92
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:450
@ RECURSE
Definition eda_item.h:51
@ ELLIPSE
Definition eda_shape.h:62
@ SEGMENT
Definition eda_shape.h:56
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
@ ELLIPSE_ARC
Definition eda_shape.h:63
static KIGFX::CONSTRUCTION_GEOM::DRAWABLE drawable(const GRAPHIC_EDIT_GEOMETRY &aGeometry)
SNAP_TARGET_ID SnapTargetId(const KIID &aId)
The snap identity of a document item.
Definition grid_helper.h:49
GRID_HELPER_GRIDS
Definition grid_helper.h:55
@ GRID_VIAS
Definition grid_helper.h:61
@ GRID_TEXT
Definition grid_helper.h:62
@ GRID_CURRENT
Definition grid_helper.h:57
@ GRID_GRAPHICS
Definition grid_helper.h:63
@ GRID_CONNECTABLE
Definition grid_helper.h:59
@ GRID_WIRES
Definition grid_helper.h:60
bool m_ExtensionSnapActivateOnHover
If extension snaps are enabled, 'activate' items on hover, even if not near a snap point.
bool m_EnableExtensionSnaps
Enable snap anchors based on item line extensions.
const wxChar *const traceSnap
Flag to enable snap/grid helper debug tracing.
std::variant< LINE, HALF_LINE, SEG, CIRCLE, SHAPE_ARC, SHAPE_ELLIPSE, BOX2I > INTERSECTABLE_GEOM
A variant type that can hold any of the supported geometry types for intersection calculations.
bool IsPcbLayer(int aLayer)
Test whether a layer is a valid layer for Pcbnew.
Definition layer_ids.h:692
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:703
@ LAYER_FOOTPRINTS_FR
Show footprints on front.
Definition layer_ids.h:255
@ LAYER_AUX_ITEMS
Auxiliary items (guides, rule, etc).
Definition layer_ids.h:279
@ LAYER_FOOTPRINTS_BK
Show footprints on back.
Definition layer_ids.h:256
@ LAYER_ANCHOR
Anchor of items having an anchor point (texts, footprints).
Definition layer_ids.h:244
@ LAYER_SUBGRIDS
Routing/placement subgrids (PCB_GRID_ITEM) visibility and color.
Definition layer_ids.h:323
bool IsInnerCopperLayer(int aLayerId)
Test whether a layer is an inner (In1_Cu to In30_Cu) copper layer.
Definition layer_ids.h:725
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ B_Cu
Definition layer_ids.h:61
@ F_Cu
Definition layer_ids.h:60
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:79
std::vector< TYPED_POINT2I > GetCircleKeyPoints(const CIRCLE &aCircle, bool aIncludeCenter)
Get key points of an CIRCLE.
std::vector< TYPED_POINT2I > GetOvalKeyPoints(const SHAPE_SEGMENT &aOval, OVAL_KEY_POINT_FLAGS aFlags)
Get a list of interesting points on an oval (rectangle with semicircular end caps)
Definition oval.cpp:46
std::array< SEG, 4 > BoxToSegs(const BOX2I &aBox)
Decompose a BOX2 into four segments.
@ OVAL_CAP_TIPS
Definition oval.h:45
@ OVAL_SIDE_MIDPOINTS
Definition oval.h:47
@ OVAL_CARDINAL_EXTREMES
Definition oval.h:49
@ OVAL_CENTER
Definition oval.h:44
unsigned int OVAL_KEY_POINT_FLAGS
Definition oval.h:53
@ GEOMETRY
Position or shape has changed.
Definition view_item.h:51
STL namespace.
VECTOR2I GetNearestPoint(const NEARABLE_GEOM &aGeom, const VECTOR2I &aPt)
Get the nearest point on a geometry to a given point.
Definition nearest.cpp:54
std::variant< LINE, HALF_LINE, SEG, CIRCLE, SHAPE_ARC, SHAPE_ELLIPSE, BOX2I, VECTOR2I > NEARABLE_GEOM
A variant type that can hold any of the supported geometry types for nearest point calculations.
Definition nearest.h:40
@ CHAMFERED_RECT
Definition padstack.h:59
@ ROUNDRECT
Definition padstack.h:56
@ TRAPEZOID
Definition padstack.h:55
@ RECTANGLE
Definition padstack.h:53
BARCODE class definition.
static bool PadstackUniqueLayerAppliesToLayer(const PADSTACK &aPadStack, PCB_LAYER_ID aPadstackUniqueLayer, const PCB_LAYER_ID aRealLayer)
PCB_GRID_ITEM * FindActiveGridAt(const BOARD &aBoard, const VECTOR2I &aPos, PCB_GRID_ROLE aRole)
Pick the grid item active for aRole at aPos.
Class to handle a set of BOARD_ITEMs.
@ PT_INTERSECTION
The point is an intersection of two (or more) items.
Definition point_types.h:63
@ PT_CENTER
The point is the center of something.
Definition point_types.h:42
@ PT_CORNER
The point is a corner of a polygon, rectangle, etc (you may want to infer PT_END from this)
Definition point_types.h:59
@ PT_NONE
No specific point type.
Definition point_types.h:38
@ PT_QUADRANT
The point is on a quadrant of a circle (N, E, S, W points).
Definition point_types.h:54
@ PT_END
The point is at the end of a segment, arc, etc.
Definition point_types.h:46
@ PT_MID
The point is at the middle of a segment, arc, etc.
Definition point_types.h:50
@ PT_ON_ELEMENT
The point is somewhere on another element, but not some specific point.
Definition point_types.h:68
std::optional< VECTOR2I > OPT_VECTOR2I
Definition seg.h:35
VECTOR2I::extended_type ecoord
Utility functions for working with shapes.
SNAP_FRAME_OUTPUT< Payload > ResolveSnapFrame(SNAP_FRAME_INPUT< Payload > aInput)
Definition snap_frame.h:60
SNAP_STABLE_ID MakePointSnapId(SNAP_ID_KIND aKind, const VECTOR2I &aPoint, int aFeatureIndex=0)
SNAP_STABLE_ID MakeCompositeSnapId(SNAP_ID_KIND aKind, const std::vector< SNAP_TARGET_ID > &aTargets, int aFeatureIndex=0)
SNAP_STABLE_ID MakeDerivedSnapId(SNAP_ID_KIND aKind, const SNAP_STABLE_ID &aSource, int aFeatureIndex=0, int aSolutionBranch=0)
SNAP_ID_KIND
Items to be used for the construction of "virtual" anchors, for example, when snapping to a point inv...
VECTOR2D Snap(const VECTOR2D &aPoint) const
Snap a point to the nearest on-grid position.
std::vector< EDA_ITEM * > items
Items that are associated with this anchor (can be more than one, e.g.
double Distance(const VECTOR2I &aP) const
World-space snap thresholds derived from the screen-space snap radius.
int range
Snap radius, optionally clamped to the visible grid.
int in
Distance at which a candidate is picked up.
double scale
SNAP_SCREEN_RADIUS in world units.
int out
Distance at which a held candidate is released.
double rankingHysteresis
Resolver ranking stickiness, as a fraction of the radius.
A visitor that visits INTERSECTABLE_GEOM variant objects with another (which is held as state: m_othe...
A single anchor point contributed by an item, before it is registered with the helper.
PCB_INTERSECTABLE(BOARD_ITEM *aItem, INTERSECTABLE_GEOM aSeg)
INTERSECTABLE_GEOM Geometry
This file contains data structures that are saved in the project file or project local settings file ...
bool otherItems
Anything not fitting one of the above categories.
bool graphics
Graphic lines, shapes, polygons.
bool footprints
Allow selecting entire footprints.
bool text
Text (free or attached to a footprint)
static SNAP_CANDIDATE Point(SNAP_STABLE_ID aId, SNAP_PRIORITY_TIER aPriority, SNAP_CANDIDATE_SUBTYPE aSubtype, const VECTOR2I &aPoint, double aResidual)
std::optional< SNAP_STABLE_ID > retainedId
Definition snap_frame.h:34
SNAP_RESOLVER::TRACE_CALLBACK trace
Definition snap_frame.h:39
SNAP_RESOLVER::FEASIBILITY_CALLBACK feasibility
Definition snap_frame.h:38
std::map< SNAP_STABLE_ID, Payload > presentation
Definition snap_frame.h:35
SNAP_SOURCE_CONTEXT context
Definition snap_frame.h:32
std::vector< SNAP_CANDIDATE > candidates
Definition snap_frame.h:33
double rankingHysteresis
Definition snap_frame.h:37
std::vector< SNAP_STABLE_ID > stickyIds
Definition snap_frame.h:36
std::optional< SNAP_FRAME_PRESENTATION< Payload > > presentation
Definition snap_frame.h:55
SNAP_RESULT result
Definition snap_frame.h:54
std::optional< VECTOR2I > stationarySourceLeg
std::optional< BOX2I > movingBounds
std::optional< VECTOR2I > movingReferencePoint
std::optional< SNAP_STABLE_ID > movingItem
std::vector< SNAP_STABLE_ID > stationarySelfFeatures
SNAP_EDITOR_PROFILE profile
SNAP_REFERENCE_PREFERENCE referencePreference
SNAP_TARGET_ID target
VECTOR2I center
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
wxString result
Test unit parsing edge cases and error handling.
wxLogTrace helper definitions.
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:227
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:98
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:95
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
@ PCB_DRILL_MAP_T
class PCB_DRILL_MAP, drill symbols drawn at the holes
Definition typeinfo.h:240
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:96
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:103
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:85
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:100
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:84
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:81
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:82
@ PCB_MARKER_T
class PCB_MARKER, a marker used to show something
Definition typeinfo.h:91
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:93
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition typeinfo.h:99
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:78
@ PCB_GRID_ITEM_T
a subgrid placed on a board
Definition typeinfo.h:238
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:94
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:90
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:92
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:86
@ PCB_POINT_T
class PCB_POINT, a 0-dimensional point
Definition typeinfo.h:105
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:88
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:97
@ PCB_DRILL_CHART_T
class PCB_DRILL_CHART, a live drill chart derived from PCB_TABLE
Definition typeinfo.h:239
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707