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, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include "pcb_grid_helper.h"
27
28#include <functional>
29#include <algorithm>
30
31#include <advanced_config.h>
32#include <pcb_dimension.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_reference_image.h>
40#include <pcb_track.h>
41#include <zone.h>
44#include <geometry/nearest.h>
45#include <geometry/oval.h>
48#include <geometry/shape_rect.h>
52#include <macros.h>
53#include <math/util.h> // for KiROUND
54#include <gal/painter.h>
55#include <pcbnew_settings.h>
56#include <tool/tool_manager.h>
57#include <tools/pcb_tool_base.h>
58#include <view/view.h>
59#include <trace_helpers.h>
60
61namespace
62{
63
69std::optional<INTERSECTABLE_GEOM> GetBoardIntersectable( const BOARD_ITEM& aItem )
70{
71 switch( aItem.Type() )
72 {
73 case PCB_SHAPE_T:
74 {
75 const PCB_SHAPE& shape = static_cast<const PCB_SHAPE&>( aItem );
76
77 switch( shape.GetShape() )
78 {
79 case SHAPE_T::SEGMENT: return SEG{ shape.GetStart(), shape.GetEnd() };
80 case SHAPE_T::CIRCLE: return CIRCLE{ shape.GetCenter(), shape.GetRadius() };
81 case SHAPE_T::ARC: return SHAPE_ARC{ shape.GetStart(), shape.GetArcMid(), shape.GetEnd(), 0 };
82 case SHAPE_T::RECTANGLE: return BOX2I::ByCorners( shape.GetStart(), shape.GetEnd() );
83 default: break;
84 }
85
86 break;
87 }
88
89 case PCB_TRACE_T:
90 {
91 const PCB_TRACK& track = static_cast<const PCB_TRACK&>( aItem );
92 return SEG{ track.GetStart(), track.GetEnd() };
93 }
94
95 case PCB_ARC_T:
96 {
97 const PCB_ARC& arc = static_cast<const PCB_ARC&>( aItem );
98 return SHAPE_ARC{ arc.GetStart(), arc.GetMid(), arc.GetEnd(), 0 };
99 }
100
102 {
103 const PCB_REFERENCE_IMAGE& refImage = static_cast<const PCB_REFERENCE_IMAGE&>( aItem );
104 return refImage.GetBoundingBox();
105 }
106
107 default:
108 break;
109 }
110
111 return std::nullopt;
112}
113
123std::optional<int64_t> FindSquareDistanceToItem( const BOARD_ITEM& item, const VECTOR2I& aPos )
124{
125 std::optional<INTERSECTABLE_GEOM> intersectable = GetBoardIntersectable( item );
126 std::optional<NEARABLE_GEOM> nearable;
127
128 if( intersectable )
129 {
130 // Exploit the intersectable as a nearable
131 std::visit(
132 [&]( const auto& geom )
133 {
134 nearable = NEARABLE_GEOM( geom );
135 },
136 *intersectable );
137 }
138
139 // Whatever the item is, we don't have a nearable for it
140 if( !nearable )
141 return std::nullopt;
142
143 const VECTOR2I nearestPt = GetNearestPoint( *nearable, aPos );
144 return nearestPt.SquaredDistance( aPos );
145}
146
147} // namespace
148
154
155
157 GRID_HELPER( aToolMgr, LAYER_ANCHOR ),
158 m_magneticSettings( aMagneticSettings )
159{
160 if( !m_toolMgr )
161 return;
162
163 KIGFX::VIEW* view = m_toolMgr->GetView();
164 KIGFX::RENDER_SETTINGS* settings = view->GetPainter()->GetSettings();
165 KIGFX::COLOR4D auxItemsColor = settings->GetLayerColor( LAYER_AUX_ITEMS );
166
167 m_viewAxis.SetSize( 20000 );
169 m_viewAxis.SetColor( auxItemsColor.WithAlpha( 0.4 ) );
170 m_viewAxis.SetDrawAtZero( true );
171 view->Add( &m_viewAxis );
172 view->SetVisible( &m_viewAxis, false );
173
174 m_viewSnapPoint.SetSize( 10 );
176 m_viewSnapPoint.SetColor( auxItemsColor );
177 m_viewSnapPoint.SetDrawAtZero( true );
178 view->Add( &m_viewSnapPoint );
179 view->SetVisible( &m_viewSnapPoint, false );
180
181 if( m_toolMgr->GetModel() )
182 static_cast<BOARD*>( aToolMgr->GetModel() )->AddListener( this );
183}
184
185
187{
188 if( !m_toolMgr )
189 return;
190
191 KIGFX::VIEW* view = m_toolMgr->GetView();
192
193 view->Remove( &m_viewAxis );
194 view->Remove( &m_viewSnapPoint );
195
196 if( m_toolMgr->GetModel() )
197 static_cast<BOARD*>( m_toolMgr->GetModel() )->RemoveListener( this );
198}
199
200
201void PCB_GRID_HELPER::AddConstructionItems( std::vector<BOARD_ITEM*> aItems, bool aExtensionOnly,
202 bool aIsPersistent )
203{
204 if( !ADVANCED_CFG::GetCfg().m_EnableExtensionSnaps )
205 return;
206
207 // For all the elements that get drawn construction geometry,
208 // add something suitable to the construction helper.
209 // This can be nothing.
210 auto constructionItemsBatch = std::make_unique<CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM_BATCH>();
211
212 std::vector<VECTOR2I> referenceOnlyPoints;
213
214 for( BOARD_ITEM* item : aItems )
215 {
216 std::vector<KIGFX::CONSTRUCTION_GEOM::DRAWABLE> constructionDrawables;
217
218 switch( item->Type() )
219 {
220 case PCB_SHAPE_T:
221 {
222 PCB_SHAPE& shape = static_cast<PCB_SHAPE&>( *item );
223
224 switch( shape.GetShape() )
225 {
226 case SHAPE_T::SEGMENT:
227 {
228 if( !aExtensionOnly )
229 {
230 constructionDrawables.emplace_back( LINE{ shape.GetStart(), shape.GetEnd() } );
231 }
232 else
233 {
234 // Two rays, extending from the segment ends
235 const VECTOR2I segVec = shape.GetEnd() - shape.GetStart();
236 constructionDrawables.emplace_back( HALF_LINE{ shape.GetStart(), shape.GetStart() - segVec } );
237 constructionDrawables.emplace_back( HALF_LINE{ shape.GetEnd(), shape.GetEnd() + segVec } );
238 }
239
240 if( aIsPersistent )
241 {
242 // include the original endpoints as construction items
243 // (this allows H/V snapping)
244 constructionDrawables.emplace_back( shape.GetStart() );
245 constructionDrawables.emplace_back( shape.GetEnd() );
246
247 // But mark them as references, so they don't get snapped to themsevles
248 referenceOnlyPoints.emplace_back( shape.GetStart() );
249 referenceOnlyPoints.emplace_back( shape.GetEnd() );
250 }
251 break;
252 }
253 case SHAPE_T::ARC:
254 {
255 if( !aExtensionOnly )
256 {
257 constructionDrawables.push_back( CIRCLE{ shape.GetCenter(), shape.GetRadius() } );
258 }
259 else
260 {
261 // The rest of the circle is the arc through the opposite point to the midpoint
262 const VECTOR2I oppositeMid = shape.GetCenter() + ( shape.GetCenter() - shape.GetArcMid() );
263 constructionDrawables.push_back( SHAPE_ARC{ shape.GetStart(), oppositeMid, shape.GetEnd(), 0 } );
264 }
265
266 constructionDrawables.push_back( shape.GetCenter() );
267
268 if( aIsPersistent )
269 {
270 // include the original endpoints as construction items
271 // (this allows H/V snapping)
272 constructionDrawables.emplace_back( shape.GetStart() );
273 constructionDrawables.emplace_back( shape.GetEnd() );
274
275 // But mark them as references, so they don't get snapped to themselves
276 referenceOnlyPoints.emplace_back( shape.GetStart() );
277 referenceOnlyPoints.emplace_back( shape.GetEnd() );
278 }
279
280 break;
281 }
282 case SHAPE_T::CIRCLE:
284 {
285 constructionDrawables.push_back( shape.GetCenter() );
286 break;
287 }
288 default:
289 // This shape doesn't have any construction geometry to draw
290 break;
291 }
292 break;
293 }
295 {
296 const PCB_REFERENCE_IMAGE& pcbRefImg = static_cast<PCB_REFERENCE_IMAGE&>( *item );
297 const REFERENCE_IMAGE& refImg = pcbRefImg.GetReferenceImage();
298
299 constructionDrawables.push_back( refImg.GetPosition() );
300
301 if( refImg.GetTransformOriginOffset() != VECTOR2I( 0, 0 ) )
302 constructionDrawables.push_back( refImg.GetPosition() + refImg.GetTransformOriginOffset() );
303
304 for( const SEG& seg : KIGEOM::BoxToSegs( refImg.GetBoundingBox() ) )
305 constructionDrawables.push_back( seg );
306
307 break;
308 }
309 default:
310 // This item doesn't have any construction geometry to draw
311 break;
312 }
313
314 // At this point, constructionDrawables can be empty, which is fine
315 // (it means there's no additional construction geometry to draw, but
316 // the item is still going to be proposed for activation)
317
318 // Convert the drawables to DRAWABLE_ENTRY format
319 std::vector<CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM::DRAWABLE_ENTRY> drawableEntries;
320 drawableEntries.reserve( constructionDrawables.size() );
321 for( auto& drawable : constructionDrawables )
322 {
323 drawableEntries.emplace_back(
325 }
326
327 constructionItemsBatch->emplace_back( CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM{
329 item,
330 std::move( drawableEntries ),
331 } );
332 }
333
334 if( referenceOnlyPoints.size() )
335 getSnapManager().SetReferenceOnlyPoints( std::move( referenceOnlyPoints ) );
336
337 // Let the manager handle it
338 getSnapManager().GetConstructionManager().ProposeConstructionItems( std::move( constructionItemsBatch ),
339 aIsPersistent );
340}
341
342
344{
345 const int c_gridSnapEpsilon_sq = 4;
346
347 VECTOR2I aligned = Align( aPoint );
348
349 if( !m_enableSnap )
350 return aligned;
351
352 std::vector<VECTOR2I> points;
353
354 const SEG testSegments[] = { SEG( aligned, aligned + VECTOR2( 1, 0 ) ),
355 SEG( aligned, aligned + VECTOR2( 0, 1 ) ),
356 SEG( aligned, aligned + VECTOR2( 1, 1 ) ),
357 SEG( aligned, aligned + VECTOR2( 1, -1 ) ) };
358
359 for( const SEG& seg : testSegments )
360 {
361 OPT_VECTOR2I vec = aSeg.IntersectLines( seg );
362
363 if( vec && aSeg.SquaredDistance( *vec ) <= c_gridSnapEpsilon_sq )
364 points.push_back( *vec );
365 }
366
367 VECTOR2I nearest = aligned;
369
370 // Snap by distance between pointer and endpoints
371 for( const VECTOR2I& pt : { aSeg.A, aSeg.B } )
372 {
373 SEG::ecoord d_sq = ( pt - aPoint ).SquaredEuclideanNorm();
374
375 if( d_sq < min_d_sq )
376 {
377 min_d_sq = d_sq;
378 nearest = pt;
379 }
380 }
381
382 // Snap by distance between aligned cursor and intersections
383 for( const VECTOR2I& pt : points )
384 {
385 SEG::ecoord d_sq = ( pt - aligned ).SquaredEuclideanNorm();
386
387 if( d_sq < min_d_sq )
388 {
389 min_d_sq = d_sq;
390 nearest = pt;
391 }
392 }
393
394 return nearest;
395}
396
397
399{
400 VECTOR2I aligned = Align( aPoint );
401
402 if( !m_enableSnap )
403 return aligned;
404
405 std::vector<VECTOR2I> points;
406
407 aArc.IntersectLine( SEG( aligned, aligned + VECTOR2( 1, 0 ) ), &points );
408 aArc.IntersectLine( SEG( aligned, aligned + VECTOR2( 0, 1 ) ), &points );
409 aArc.IntersectLine( SEG( aligned, aligned + VECTOR2( 1, 1 ) ), &points );
410 aArc.IntersectLine( SEG( aligned, aligned + VECTOR2( 1, -1 ) ), &points );
411
412 VECTOR2I nearest = aligned;
414
415 // Snap by distance between pointer and endpoints
416 for( const VECTOR2I& pt : { aArc.GetP0(), aArc.GetP1() } )
417 {
418 SEG::ecoord d_sq = ( pt - aPoint ).SquaredEuclideanNorm();
419
420 if( d_sq < min_d_sq )
421 {
422 min_d_sq = d_sq;
423 nearest = pt;
424 }
425 }
426
427 // Snap by distance between aligned cursor and intersections
428 for( const VECTOR2I& pt : points )
429 {
430 SEG::ecoord d_sq = ( pt - aligned ).SquaredEuclideanNorm();
431
432 if( d_sq < min_d_sq )
433 {
434 min_d_sq = d_sq;
435 nearest = pt;
436 }
437 }
438
439 return nearest;
440}
441
442
443VECTOR2I PCB_GRID_HELPER::SnapToPad( const VECTOR2I& aMousePos, std::deque<PAD*>& aPads )
444{
445 clearAnchors();
446
447 for( BOARD_ITEM* item : aPads )
448 {
449 if( item->HitTest( aMousePos ) )
450 computeAnchors( item, aMousePos, true, nullptr );
451 }
452
453 double minDist = std::numeric_limits<double>::max();
454 ANCHOR* nearestOrigin = nullptr;
455
456 for( ANCHOR& a : m_anchors )
457 {
458 if( ( ORIGIN & a.flags ) != ORIGIN )
459 continue;
460
461 double dist = a.Distance( aMousePos );
462
463 if( dist < minDist )
464 {
465 minDist = dist;
466 nearestOrigin = &a;
467 }
468 }
469
470 return nearestOrigin ? nearestOrigin->pos : aMousePos;
471}
472
473
475 std::vector<BOARD_ITEM*>& aItems,
476 GRID_HELPER_GRIDS aGrid,
477 const PCB_SELECTION_FILTER_OPTIONS* aSelectionFilter )
478{
479 clearAnchors();
480
481 computeAnchors( aItems, aMousePos, true, aSelectionFilter, nullptr, true );
482
483 double lineSnapMinCornerDistance = m_toolMgr->GetView()->ToWorld( 50 );
484
485 ANCHOR* nearestOutline = nearestAnchor( aMousePos, OUTLINE );
486 ANCHOR* nearestCorner = nearestAnchor( aMousePos, CORNER );
487 ANCHOR* nearestOrigin = nearestAnchor( aMousePos, ORIGIN );
488 ANCHOR* best = nullptr;
489 double minDist = std::numeric_limits<double>::max();
490
491 if( nearestOrigin )
492 {
493 minDist = nearestOrigin->Distance( aMousePos );
494 best = nearestOrigin;
495 }
496
497 if( nearestCorner )
498 {
499 double dist = nearestCorner->Distance( aMousePos );
500
501 if( dist < minDist )
502 {
503 minDist = dist;
504 best = nearestCorner;
505 }
506 }
507
508 if( nearestOutline )
509 {
510 double dist = nearestOutline->Distance( aMousePos );
511
512 if( minDist > lineSnapMinCornerDistance && dist < minDist )
513 best = nearestOutline;
514 }
515
516 return best ? best->pos : aMousePos;
517}
518
519
521 GRID_HELPER_GRIDS aGrid )
522{
523 LSET layers;
524 std::vector<BOARD_ITEM*> item;
525
526 if( aReferenceItem )
527 {
528 layers = aReferenceItem->GetLayerSet();
529 item.push_back( aReferenceItem );
530 }
531 else
532 {
533 layers = LSET::AllLayersMask();
534 }
535
536 return BestSnapAnchor( aOrigin, layers, aGrid, item );
537}
538
539
541 GRID_HELPER_GRIDS aGrid,
542 const std::vector<BOARD_ITEM*>& aSkip )
543{
544 wxLogTrace( traceSnap, "BestSnapAnchor: origin (%d, %d), enableSnap=%d, enableGrid=%d, enableSnapLine=%d",
545 aOrigin.x, aOrigin.y, m_enableSnap, m_enableGrid, m_enableSnapLine );
546
547 // Tuning constant: snap radius in screen space
548 const int snapSize = 25;
549
550 // Snapping distance is in screen space, clamped to the current grid to ensure that the grid
551 // points that are visible can always be snapped to.
552 // see https://gitlab.com/kicad/code/kicad/-/issues/5638
553 // see https://gitlab.com/kicad/code/kicad/-/issues/7125
554 // see https://gitlab.com/kicad/code/kicad/-/issues/12303
555 double snapScale = m_toolMgr->GetView()->ToWorld( snapSize );
556 // warning: GetVisibleGrid().x sometimes returns a value > INT_MAX. Intermediate calculation
557 // needs double.
558 int snapRange = KiROUND( m_enableGrid ? std::min( snapScale, GetVisibleGrid().x ) : snapScale );
559
560 //Respect limits of coordinates representation
561 const BOX2I visibilityHorizon =
562 BOX2ISafe( VECTOR2D( aOrigin ) - snapRange / 2.0, VECTOR2D( snapRange, snapRange ) );
563
564 clearAnchors();
565
566 const std::vector<BOARD_ITEM*> visibleItems = queryVisible( visibilityHorizon, aSkip );
567 computeAnchors( visibleItems, aOrigin, false, nullptr, &aLayers, false );
568
569 ANCHOR* nearest = nearestAnchor( aOrigin, SNAPPABLE );
570 VECTOR2I nearestGrid = Align( aOrigin, aGrid );
571 const VECTOR2D gridSize = GetGridSize( aGrid );
572
573 const int hysteresisWorld =
574 KiROUND( m_toolMgr->GetView()->ToWorld( ADVANCED_CFG::GetCfg().m_SnapHysteresis ) );
575 const int snapIn = std::max( 0, snapRange - hysteresisWorld );
576 const int snapOut = snapRange + hysteresisWorld;
577
578 wxLogTrace( traceSnap, " snapRange=%d, snapIn=%d, snapOut=%d, hysteresis=%d",
579 snapRange, snapIn, snapOut, hysteresisWorld );
580 wxLogTrace( traceSnap, " visibleItems count=%zu, anchors count=%zu",
581 visibleItems.size(), m_anchors.size() );
582 wxLogTrace( traceSnap, " nearest anchor: %s at (%d, %d), distance=%f",
583 nearest ? "found" : "none",
584 nearest ? nearest->pos.x : 0,
585 nearest ? nearest->pos.y : 0,
586 nearest ? nearest->Distance( aOrigin ) : -1.0 );
587 wxLogTrace( traceSnap, " nearestGrid: (%d, %d)", nearestGrid.x, nearestGrid.y );
588
590 {
591 ad->ClearAnchors();
592
593 for( const ANCHOR& anchor : m_anchors )
594 ad->AddAnchor( anchor.pos );
595
596 ad->SetNearest( nearest ? OPT_VECTOR2I{ nearest->pos } : std::nullopt );
597 m_toolMgr->GetView()->Update( ad, KIGFX::GEOMETRY );
598 }
599
600 // The distance to the nearest snap point, if any
601 std::optional<int> snapDist;
602
603 if( nearest )
604 snapDist = nearest->Distance( aOrigin );
605
606 if( m_snapItem )
607 {
608 int existingDist = m_snapItem->Distance( aOrigin );
609 if( !snapDist || existingDist < *snapDist )
610 snapDist = existingDist;
611 }
612
613 wxLogTrace( traceSnap, " snapDist: %s (value=%d)",
614 snapDist ? "set" : "none", snapDist ? *snapDist : -1 );
615 wxLogTrace( traceSnap, " m_snapItem: %s", m_snapItem ? "exists" : "none" );
616
618
619 SNAP_MANAGER& snapManager = getSnapManager();
620 SNAP_LINE_MANAGER& snapLineManager = snapManager.GetSnapLineManager();
621
622 const auto ptIsReferenceOnly =
623 [&]( const VECTOR2I& aPt )
624 {
625 const std::vector<VECTOR2I>& referenceOnlyPoints = snapManager.GetReferenceOnlyPoints();
626 return std::find( referenceOnlyPoints.begin(), referenceOnlyPoints.end(), aPt )
627 != referenceOnlyPoints.end();
628 };
629
630 const auto proposeConstructionForItems =
631 [&]( const std::vector<EDA_ITEM*>& aItems )
632 {
633 // Add any involved item as a temporary construction item
634 // (de-duplication with existing construction items is handled later)
635 std::vector<BOARD_ITEM*> items;
636
637 for( EDA_ITEM* item : aItems )
638 {
639 if( !item->IsBOARD_ITEM() )
640 continue;
641
642 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
643
644 // Null items are allowed to arrive here as they represent geometry that isn't
645 // specifically tied to a board item. For example snap lines from some
646 // other anchor.
647 // But they don't produce new construction items.
648 if( boardItem )
649 {
650 if( m_magneticSettings->allLayers || ( ( aLayers & boardItem->GetLayerSet() ).any() ) )
651 items.push_back( boardItem );
652 }
653 }
654
655 // Temporary construction items are not persistent and don't
656 // overlay the items themselves (as the items will not be moved)
657 AddConstructionItems( items, true, false );
658 };
659
660 bool snapValid = false;
661
662 if( m_enableSnap )
663 {
664 wxLogTrace( traceSnap, " Snap enabled, checking snap options..." );
665
666 // Existing snap lines need priority over new snaps
667 if( m_enableSnapLine )
668 {
669 wxLogTrace( traceSnap, " Checking snap lines..." );
670
671 OPT_VECTOR2I snapLineSnap = snapLineManager.GetNearestSnapLinePoint(
672 aOrigin, nearestGrid, snapDist, snapRange, gridSize, GetOrigin() );
673
674 if( !snapLineSnap )
675 {
676 std::optional<VECTOR2I> constructionSnap =
677 SnapToConstructionLines( aOrigin, nearestGrid, gridSize, snapRange );
678
679 if( constructionSnap )
680 snapLineSnap = *constructionSnap;
681 }
682
683 // We found a better snap point that the nearest one
684 if( snapLineSnap && m_skipPoint != *snapLineSnap )
685 {
686 wxLogTrace( traceSnap, " Snap line found at (%d, %d)",
687 snapLineSnap->x, snapLineSnap->y );
688
689 // Check if we have a nearby anchor that should take precedence
690 // Prefer actual anchors over construction line grid intersections
691 bool preferAnchor = false;
692 if( nearest && nearest->Distance( aOrigin ) <= snapIn )
693 {
694 preferAnchor = true;
695 wxLogTrace( traceSnap, " Preferring anchor over snap line (anchorDist=%f, snapRange=%d)",
696 nearest->Distance( aOrigin ), snapRange );
697 }
698 else
699 {
700 if( nearest )
701 {
702 wxLogTrace( traceSnap, " Nearest anchor at (%d, %d), distance=%f is out of range (snapRange=%d)",
703 nearest->pos.x, nearest->pos.y,
704 nearest->Distance( aOrigin ),
705 snapRange );
706 }
707 else
708 {
709 wxLogTrace( traceSnap, " No nearest anchor to consider" );
710 }
711 }
712
713 if( !preferAnchor )
714 {
715 snapLineManager.SetSnapLineEnd( *snapLineSnap );
716 snapValid = true;
717
718 // Don't show a snap point if we're snapping to a grid rather than an anchor
719 m_toolMgr->GetView()->SetVisible( &m_viewSnapPoint, false );
720 m_viewSnapPoint.SetSnapTypes( POINT_TYPE::PT_NONE );
721
722 // Only return the snap line end as a snap if it's not a reference point
723 // (we don't snap to reference points, but we can use them to update the snap line,
724 // without actually snapping)
725 if( !ptIsReferenceOnly( *snapLineSnap ) )
726 {
727 wxLogTrace( traceSnap, " RETURNING snap line point (non-reference): (%d, %d)",
728 snapLineSnap->x, snapLineSnap->y );
729 return *snapLineSnap;
730 }
731 else
732 {
733 wxLogTrace( traceSnap, " Snap line point is reference-only, continuing..." );
734 }
735 }
736 else
737 {
738 wxLogTrace( traceSnap, " Skipping snap line, will use anchor instead" );
739 }
740 }
741 }
742
743 if( m_snapItem )
744 {
745 int dist = m_snapItem->Distance( aOrigin );
746
747 wxLogTrace( traceSnap, " Checking existing m_snapItem, dist=%d (snapOut=%d)",
748 dist, snapOut );
749
750 if( dist <= snapOut )
751 {
752 if( nearest && ptIsReferenceOnly( nearest->pos ) &&
753 nearest->Distance( aOrigin ) <= snapRange )
754 snapLineManager.SetSnapLineOrigin( nearest->pos );
755
756 snapLineManager.SetSnappedAnchor( m_snapItem->pos );
757 updateSnapPoint( { m_snapItem->pos, m_snapItem->pointTypes } );
758
759 wxLogTrace( traceSnap, " RETURNING existing m_snapItem: (%d, %d)",
760 m_snapItem->pos.x, m_snapItem->pos.y );
761 return m_snapItem->pos;
762 }
763
764 wxLogTrace( traceSnap, " m_snapItem too far, clearing..." );
765 m_snapItem = std::nullopt;
766 }
767
768 // If there's a snap anchor within range, use it if we can
769 if( nearest && nearest->Distance( aOrigin ) <= snapIn )
770 {
771 wxLogTrace( traceSnap, " Nearest anchor within snapIn range" );
772
773 const bool anchorIsConstructed = nearest->flags & ANCHOR_FLAGS::CONSTRUCTED;
774
775 // If the nearest anchor is a reference point, we don't snap to it,
776 // but we can update the snap line origin
777 if( ptIsReferenceOnly( nearest->pos ) )
778 {
779 wxLogTrace( traceSnap, " Nearest anchor is reference-only, setting snap line origin" );
780 // We can set the snap line origin, but don't mess with the
781 // accepted snap point
782 snapLineManager.SetSnapLineOrigin( nearest->pos );
783 }
784 else
785 {
786 wxLogTrace( traceSnap, " Nearest anchor accepted, constructed=%d", anchorIsConstructed );
787
788 // 'Intrinsic' points of items can trigger adding construction geometry
789 // for _that_ item by proximity. E.g. just mousing over the intersection
790 // of an item doesn't add a construction item for the second item).
791 // This is to make construction items less intrusive and more
792 // a result of user intent.
793 if( !anchorIsConstructed )
794 proposeConstructionForItems( nearest->items );
795
796 m_snapItem = *nearest;
797
798 // Set the snap line origin or end as needed
799 snapLineManager.SetSnappedAnchor( m_snapItem->pos );
800 // Show the correct snap point marker
801 updateSnapPoint( { m_snapItem->pos, m_snapItem->pointTypes } );
802
803 wxLogTrace( traceSnap, " RETURNING nearest anchor: (%d, %d)",
804 m_snapItem->pos.x, m_snapItem->pos.y );
805 return m_snapItem->pos;
806 }
807
808 snapValid = true;
809 }
810 else
811 {
812 wxLogTrace( traceSnap, " No nearest anchor within snapIn range" );
813
814 static const bool canActivateByHitTest = ADVANCED_CFG::GetCfg().m_ExtensionSnapActivateOnHover;
815
816 if( canActivateByHitTest )
817 {
818 wxLogTrace( traceSnap, " Checking hit test for construction activation..." );
819
820 // An exact hit on an item, even if not near a snap point
821 // If it's tool hard to hit by hover, this can be increased
822 // to make it non-exact.
823 const int hoverAccuracy = 0;
824
825 for( BOARD_ITEM* item : visibleItems )
826 {
827 if( item->HitTest( aOrigin, hoverAccuracy ) )
828 {
829 wxLogTrace( traceSnap, " Hit item, proposing construction geometry" );
830 proposeConstructionForItems( { item } );
831 snapValid = true;
832 break;
833 }
834 }
835 }
836 }
837
838 // If we got here, we didn't snap to an anchor or snap line
839
840 // If we're snapping to a grid, on-element snaps would be too intrusive
841 // but they're useful when there isn't a grid to snap to
842 if( !m_enableGrid )
843 {
844 wxLogTrace( traceSnap, " Grid disabled, checking point-on-element snap..." );
845
846 OPT_VECTOR2I nearestPointOnAnElement = GetNearestPoint( m_pointOnLineCandidates, aOrigin );
847
848 // Got any nearest point - snap if in range
849 if( nearestPointOnAnElement && nearestPointOnAnElement->Distance( aOrigin ) <= snapRange )
850 {
851 wxLogTrace( traceSnap, " RETURNING point-on-element: (%d, %d)",
852 nearestPointOnAnElement->x, nearestPointOnAnElement->y );
853
854 updateSnapPoint( { *nearestPointOnAnElement, POINT_TYPE::PT_ON_ELEMENT } );
855
856 // Clear the snap end, but keep the origin so touching another line
857 // doesn't kill a snap line
858 snapLineManager.SetSnapLineEnd( std::nullopt );
859 return *nearestPointOnAnElement;
860 }
861 }
862 }
863
864 // Completely failed to find any snap point, so snap to the grid
865
866 wxLogTrace( traceSnap, " RETURNING grid snap: (%d, %d)", nearestGrid.x, nearestGrid.y );
867
868 m_snapItem = std::nullopt;
869
870 if( !snapValid )
872
873 snapLineManager.SetSnapLineEnd( std::nullopt );
874
875 m_toolMgr->GetView()->SetVisible( &m_viewSnapPoint, false );
876
877 return nearestGrid;
878}
879
880
882{
883 if( !m_snapItem )
884 return nullptr;
885
886 // The snap anchor doesn't have an item associated with it
887 // (odd, could it be entirely made of construction geometry?)
888 if( m_snapItem->items.empty() )
889 return nullptr;
890
891 return static_cast<BOARD_ITEM*>( m_snapItem->items[0] );
892}
893
894
896{
897 if( !aItem )
898 return GRID_CURRENT;
899
900 switch( aItem->Type() )
901 {
902 case PCB_FOOTPRINT_T:
903 case PCB_PAD_T:
904 return GRID_CONNECTABLE;
905
906 case PCB_TEXT_T:
907 case PCB_FIELD_T:
908 return GRID_TEXT;
909
910 case PCB_SHAPE_T:
911 case PCB_DIMENSION_T:
913 case PCB_TEXTBOX_T:
914 case PCB_BARCODE_T:
915 return GRID_GRAPHICS;
916
917 case PCB_TRACE_T:
918 case PCB_ARC_T:
919 return GRID_WIRES;
920
921 case PCB_VIA_T:
922 return GRID_VIAS;
923
924 default:
925 return GRID_CURRENT;
926 }
927}
928
929
931{
932 const GRID_SETTINGS& grid = m_toolMgr->GetSettings()->m_Window.grid;
933 int idx = -1;
934
935 VECTOR2D g = m_toolMgr->GetView()->GetGAL()->GetGridSize();
936
937 if( !grid.overrides_enabled )
938 return g;
939
940 switch( aGrid )
941 {
942 case GRID_CONNECTABLE:
943 if( grid.override_connected )
944 idx = grid.override_connected_idx;
945
946 break;
947
948 case GRID_WIRES:
949 if( grid.override_wires )
950 idx = grid.override_wires_idx;
951
952 break;
953
954 case GRID_VIAS:
955 if( grid.override_vias )
956 idx = grid.override_vias_idx;
957
958 break;
959
960 case GRID_TEXT:
961 if( grid.override_text )
962 idx = grid.override_text_idx;
963
964 break;
965
966 case GRID_GRAPHICS:
967 if( grid.override_graphics )
968 idx = grid.override_graphics_idx;
969
970 break;
971
972 default:
973 break;
974 }
975
976 if( idx >= 0 && idx < (int) grid.grids.size() )
977 g = grid.grids[idx].ToDouble( pcbIUScale );
978
979 return g;
980}
981
982
983std::vector<BOARD_ITEM*>
984PCB_GRID_HELPER::queryVisible( const BOX2I& aArea, const std::vector<BOARD_ITEM*>& aSkip ) const
985{
986 std::set<BOARD_ITEM*> items;
987 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> visibleItems;
988
989 PCB_TOOL_BASE* currentTool = static_cast<PCB_TOOL_BASE*>( m_toolMgr->GetCurrentTool() );
990 KIGFX::VIEW* view = m_toolMgr->GetView();
991 RENDER_SETTINGS* settings = view->GetPainter()->GetSettings();
992 const std::set<int>& activeLayers = settings->GetHighContrastLayers();
993 bool isHighContrast = settings->GetHighContrast();
994
995 view->Query( aArea, visibleItems );
996
997 for( const auto& [ viewItem, layer ] : visibleItems )
998 {
999 if( !viewItem->IsBOARD_ITEM() )
1000 continue;
1001
1002 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( viewItem );
1003
1004 if( currentTool->IsFootprintEditor() )
1005 {
1006 // If we are in the footprint editor, don't use the footprint itself
1007 if( boardItem->Type() == PCB_FOOTPRINT_T )
1008 continue;
1009 }
1010 else
1011 {
1012 // If we are not in the footprint editor, don't use footprint-editor-private items
1013 if( FOOTPRINT* parentFP = boardItem->GetParentFootprint() )
1014 {
1015 if( IsPcbLayer( layer ) && parentFP->GetPrivateLayers().test( layer ) )
1016 continue;
1017 }
1018 }
1019
1020 // The boardItem must be visible and on an active layer
1021 if( view->IsVisible( boardItem )
1022 && ( !isHighContrast || activeLayers.count( layer ) )
1023 && boardItem->ViewGetLOD( layer, view ) < view->GetScale() )
1024 {
1025 items.insert ( boardItem );
1026 }
1027 }
1028
1029 std::function<void( BOARD_ITEM* )> skipItem =
1030 [&]( BOARD_ITEM* aItem )
1031 {
1032 items.erase( aItem );
1033
1034 aItem->RunOnChildren(
1035 [&]( BOARD_ITEM* aChild )
1036 {
1037 skipItem( aChild );
1038 },
1040 };
1041
1042 for( BOARD_ITEM* item : aSkip )
1043 skipItem( item );
1044
1045 return {items.begin(), items.end()};
1046}
1047
1048
1050{
1053
1054 // Clang wants this constructor
1056 Item( aItem ),
1057 Geometry( std::move( aSeg ) )
1058 {
1059 }
1060};
1061
1062
1063void PCB_GRID_HELPER::computeAnchors( const std::vector<BOARD_ITEM*>& aItems,
1064 const VECTOR2I& aRefPos, bool aFrom,
1065 const PCB_SELECTION_FILTER_OPTIONS* aSelectionFilter,
1066 const LSET* aMatchLayers, bool aForDrag )
1067{
1068 std::vector<PCB_INTERSECTABLE> intersectables;
1069
1070 // These could come from a more granular snap mode filter
1071 // But when looking for drag points, we don't want construction geometry
1072 const bool computeIntersections = !aForDrag;
1073 const bool computePointsOnElements = !aForDrag;
1074 const bool excludeGraphics = aSelectionFilter && !aSelectionFilter->graphics;
1075 const bool excludeTracks = aSelectionFilter && !aSelectionFilter->tracks;
1076
1077 const auto itemIsSnappable =
1078 [&]( const BOARD_ITEM& aItem )
1079 {
1080 // If we are filtering by layers, check if the item matches
1081 if( aMatchLayers )
1082 return m_magneticSettings->allLayers || ( ( *aMatchLayers & aItem.GetLayerSet() ).any() );
1083
1084 return true;
1085 };
1086
1087 const auto processItem =
1088 [&]( BOARD_ITEM& item )
1089 {
1090 // Don't even process the item if it doesn't match the layers
1091 if( !itemIsSnappable( item ) )
1092 return;
1093
1094 // First, add all the key points of the item itself
1095 computeAnchors( &item, aRefPos, aFrom, aSelectionFilter );
1096
1097 // If we are computing intersections, construct the relevant intersectables
1098 // Points on elements also use the intersectables.
1099 if( computeIntersections || computePointsOnElements )
1100 {
1101 std::optional<INTERSECTABLE_GEOM> intersectableGeom;
1102
1103 if( !excludeGraphics
1104 && ( item.Type() == PCB_SHAPE_T || item.Type() == PCB_REFERENCE_IMAGE_T ) )
1105 {
1106 intersectableGeom = GetBoardIntersectable( item );
1107 }
1108 else if( !excludeTracks && ( item.Type() == PCB_TRACE_T || item.Type() == PCB_ARC_T ) )
1109 {
1110 intersectableGeom = GetBoardIntersectable( item );
1111 }
1112
1113 if( intersectableGeom )
1114 intersectables.emplace_back( &item, *intersectableGeom );
1115 }
1116 };
1117
1118 for( BOARD_ITEM* item : aItems )
1119 {
1120 processItem( *item );
1121 }
1122
1123 for( const CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM_BATCH& batch : getSnapManager().GetConstructionItems() )
1124 {
1125 for( const CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM& constructionItem : batch )
1126 {
1127 BOARD_ITEM* involvedItem = static_cast<BOARD_ITEM*>( constructionItem.Item );
1128
1129 for( const CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM::DRAWABLE_ENTRY& drawable : constructionItem.Constructions )
1130 {
1131 std::visit(
1132 [&]( const auto& visited )
1133 {
1134 using ItemType = std::decay_t<decltype( visited )>;
1135
1136 if constexpr( std::is_same_v<ItemType, LINE>
1137 || std::is_same_v<ItemType, CIRCLE>
1138 || std::is_same_v<ItemType, HALF_LINE>
1139 || std::is_same_v<ItemType, SHAPE_ARC> )
1140 {
1141 intersectables.emplace_back( involvedItem, visited );
1142 }
1143 else if constexpr( std::is_same_v<ItemType, VECTOR2I> )
1144 {
1145 // Add any free-floating points as snap points.
1146 addAnchor( visited, SNAPPABLE | CONSTRUCTED, involvedItem, POINT_TYPE::PT_NONE );
1147 }
1148 },
1149 drawable.Drawable );
1150 }
1151 }
1152 }
1153
1154 // Now, add all the intersections between the items
1155 // This is obviously quadratic, so performance may be a concern for large selections
1156 // But, so far up to ~20k comparisons seems not to be an issue with run times in the ms range
1157 // and it's usually only a handful of items.
1158
1159 if( computeIntersections )
1160 {
1161 for( std::size_t ii = 0; ii < intersectables.size(); ++ii )
1162 {
1163 const PCB_INTERSECTABLE& intersectableA = intersectables[ii];
1164
1165 for( std::size_t jj = ii + 1; jj < intersectables.size(); ++jj )
1166 {
1167 const PCB_INTERSECTABLE& intersectableB = intersectables[jj];
1168
1169 // An item and its own extension will often have intersections (as they are on top of each other),
1170 // but they not useful points to snap to
1171 if( intersectableA.Item == intersectableB.Item )
1172 continue;
1173
1174 std::vector<VECTOR2I> intersections;
1175 const INTERSECTION_VISITOR visitor{ intersectableA.Geometry, intersections };
1176
1177 std::visit( visitor, intersectableB.Geometry );
1178
1179 // For each intersection, add an intersection snap anchor
1180 for( const VECTOR2I& intersection : intersections )
1181 {
1182 std::vector<EDA_ITEM*> items = {
1183 intersectableA.Item,
1184 intersectableB.Item,
1185 };
1186 addAnchor( intersection, SNAPPABLE | CONSTRUCTED, std::move( items ),
1188 }
1189 }
1190 }
1191 }
1192
1193 // The intersectables can also be used for fall-back snapping to "point on line"
1194 // snaps if no other snap is found
1196
1197 if( computePointsOnElements )
1198 {
1199 // For the moment, it's trivial to make a NEARABLE from an INTERSECTABLE,
1200 // because all INTERSECTABLEs are also NEARABLEs.
1201 for( const PCB_INTERSECTABLE& intersectable : intersectables )
1202 {
1203 std::visit(
1204 [&]( const auto& geom )
1205 {
1206 NEARABLE_GEOM nearable( geom );
1207 m_pointOnLineCandidates.emplace_back( nearable );
1208 },
1209 intersectable.Geometry );
1210 }
1211 }
1212}
1213
1214
1215// Padstacks report a set of "unique" layers, which may each represent one or more
1216// "real" layers. This function takes a unique layer and checks if it applies to the
1217// given "real" layer.
1218static bool PadstackUniqueLayerAppliesToLayer( const PADSTACK& aPadStack, PCB_LAYER_ID aPadstackUniqueLayer,
1219 const PCB_LAYER_ID aRealLayer )
1220{
1221 switch( aPadStack.Mode() )
1222 {
1224 {
1225 // Normal mode padstacks are the same on every layer, so they'll apply to any
1226 // "real" copper layer.
1227 return IsCopperLayer( aRealLayer );
1228 }
1230 {
1231 switch( aPadstackUniqueLayer )
1232 {
1233 case F_Cu:
1234 case B_Cu:
1235 // The outer-layer uhique layers only apply to those exact "real" layers
1236 return aPadstackUniqueLayer == aRealLayer;
1238 // But the inner layers apply to any inner layer
1239 return IsInnerCopperLayer( aRealLayer );
1240 default:
1241 wxFAIL_MSG( wxString::Format( "Unexpected padstack unique layer %d in FRONT_INNER_BACK mode",
1242 aPadstackUniqueLayer ) );
1243 break;
1244 }
1245 break;
1246 }
1248 {
1249 // Custom modes are unique per layer, so it's 1:1
1250 return aRealLayer == aPadstackUniqueLayer;
1251 }
1252 }
1253
1254 return false;
1255};
1256
1257
1258void PCB_GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos, bool aFrom,
1259 const PCB_SELECTION_FILTER_OPTIONS* aSelectionFilter )
1260{
1261 KIGFX::VIEW* view = m_toolMgr->GetView();
1262 RENDER_SETTINGS* settings = view->GetPainter()->GetSettings();
1263 const std::set<int>& activeLayers = settings->GetHighContrastLayers();
1264 const PCB_LAYER_ID activeHighContrastPrimaryLayer = settings->GetPrimaryHighContrastLayer();
1265 bool isHighContrast = settings->GetHighContrast();
1266
1267 const auto checkVisibility =
1268 [&]( const BOARD_ITEM* item )
1269 {
1270 // New moved items don't yet have view flags so VIEW will call them invisible
1271 if( !view->IsVisible( item ) && !item->IsMoving() )
1272 return false;
1273
1274 bool onActiveLayer = !isHighContrast;
1275 bool isLODVisible = false;
1276
1277 for( PCB_LAYER_ID layer : item->GetLayerSet() )
1278 {
1279 if( !onActiveLayer && activeLayers.count( layer ) )
1280 onActiveLayer = true;
1281
1282 if( !isLODVisible && item->ViewGetLOD( layer, view ) < view->GetScale() )
1283 isLODVisible = true;
1284
1285 if( onActiveLayer && isLODVisible )
1286 return true;
1287 }
1288
1289 return false;
1290 };
1291
1292 // As defaults, these are probably reasonable to avoid spamming key points
1293 const KIGEOM::OVAL_KEY_POINT_FLAGS ovalKeyPointFlags = KIGEOM::OVAL_CENTER
1297
1298 auto handlePadShape =
1299 [&]( PAD* aPad, PCB_LAYER_ID aLayer )
1300 {
1302
1304 if( aFrom )
1305 return;
1306
1307 switch( aPad->GetShape( aLayer ) )
1308 {
1309 case PAD_SHAPE::CIRCLE:
1310 {
1311 const CIRCLE circle( aPad->ShapePos( aLayer ), aPad->GetSizeX() / 2 );
1312
1313 for( const TYPED_POINT2I& pt : KIGEOM::GetCircleKeyPoints( circle, false ) )
1314 addAnchor( pt.m_point, OUTLINE | SNAPPABLE, aPad, pt.m_types );
1315
1316 break;
1317 }
1318 case PAD_SHAPE::OVAL:
1319 {
1321 aPad->GetSize( aLayer ), aPad->GetPosition(), aPad->GetOrientation() );
1322
1323 for( const TYPED_POINT2I& pt : KIGEOM::GetOvalKeyPoints( oval, ovalKeyPointFlags ) )
1324 addAnchor( pt.m_point, OUTLINE | SNAPPABLE, aPad, pt.m_types );
1325
1326 break;
1327 }
1332 {
1333 VECTOR2I half_size( aPad->GetSize( aLayer ) / 2 );
1334 VECTOR2I trap_delta( 0, 0 );
1335
1336 if( aPad->GetShape( aLayer ) == PAD_SHAPE::TRAPEZOID )
1337 trap_delta = aPad->GetDelta( aLayer ) / 2;
1338
1339 SHAPE_LINE_CHAIN corners;
1340
1341 corners.Append( -half_size.x - trap_delta.y, half_size.y + trap_delta.x );
1342 corners.Append( half_size.x + trap_delta.y, half_size.y - trap_delta.x );
1343 corners.Append( half_size.x - trap_delta.y, -half_size.y + trap_delta.x );
1344 corners.Append( -half_size.x + trap_delta.y, -half_size.y - trap_delta.x );
1345 corners.SetClosed( true );
1346
1347 corners.Rotate( aPad->GetOrientation() );
1348 corners.Move( aPad->ShapePos( aLayer ) );
1349
1350 for( std::size_t ii = 0; ii < corners.GetSegmentCount(); ++ii )
1351 {
1352 const SEG& seg = corners.GetSegment( ii );
1355
1356 if( ii == corners.GetSegmentCount() - 1 )
1358 }
1359
1360 break;
1361 }
1362
1363 default:
1364 {
1365 const auto& outline = aPad->GetEffectivePolygon( aLayer, ERROR_INSIDE );
1366
1367 if( !outline->IsEmpty() )
1368 {
1369 for( const VECTOR2I& pt : outline->Outline( 0 ).CPoints() )
1370 addAnchor( pt, OUTLINE | SNAPPABLE, aPad );
1371 }
1372
1373 break;
1374 }
1375 }
1376
1377 if( aPad->HasHole() )
1378 {
1379 // Holes are at the pad centre (it's the shape that may be offset)
1380 const VECTOR2I hole_pos = aPad->GetPosition();
1381 const VECTOR2I hole_size = aPad->GetDrillSize();
1382
1383 std::vector<TYPED_POINT2I> snap_pts;
1384
1385 if( hole_size.x == hole_size.y )
1386 {
1387 // Circle
1388 const CIRCLE circle( hole_pos, hole_size.x / 2 );
1389 snap_pts = KIGEOM::GetCircleKeyPoints( circle, true );
1390 }
1391 else
1392 {
1393 // Oval
1394
1395 // For now there's no way to have an off-angle hole, so this is the
1396 // same as the pad. In future, this may not be true:
1397 // https://gitlab.com/kicad/code/kicad/-/issues/4124
1398 const SHAPE_SEGMENT oval =
1399 SHAPE_SEGMENT::BySizeAndCenter( hole_size, hole_pos, aPad->GetOrientation() );
1400 snap_pts = KIGEOM::GetOvalKeyPoints( oval, ovalKeyPointFlags );
1401 }
1402
1403 for( const TYPED_POINT2I& snap_pt : snap_pts )
1404 addAnchor( snap_pt.m_point, OUTLINE | SNAPPABLE, aPad, snap_pt.m_types );
1405 }
1406 };
1407
1408 const auto addRectPoints =
1409 [&]( const BOX2I& aBox, EDA_ITEM& aRelatedItem )
1410 {
1411 const VECTOR2I topRight( aBox.GetRight(), aBox.GetTop() );
1412 const VECTOR2I bottomLeft( aBox.GetLeft(), aBox.GetBottom() );
1413
1414 const SEG first( aBox.GetOrigin(), topRight );
1415 const SEG second( topRight, aBox.GetEnd() );
1416 const SEG third( aBox.GetEnd(), bottomLeft );
1417 const SEG fourth( bottomLeft, aBox.GetOrigin() );
1418
1419 const int snapFlags = CORNER | SNAPPABLE;
1420
1421 addAnchor( aBox.GetCenter(), snapFlags, &aRelatedItem, POINT_TYPE::PT_CENTER );
1422
1423 addAnchor( first.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER );
1424 addAnchor( first.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID );
1425 addAnchor( second.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER );
1426 addAnchor( second.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID );
1427 addAnchor( third.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER );
1428 addAnchor( third.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID );
1429 addAnchor( fourth.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER );
1430 addAnchor( fourth.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID );
1431 };
1432
1433 const auto handleShape =
1434 [&]( PCB_SHAPE* shape )
1435 {
1436 VECTOR2I start = shape->GetStart();
1437 VECTOR2I end = shape->GetEnd();
1438
1439 switch( shape->GetShape() )
1440 {
1441 case SHAPE_T::CIRCLE:
1442 {
1443 const int r = ( start - end ).EuclideanNorm();
1444
1445 addAnchor( start, ORIGIN | SNAPPABLE, shape, POINT_TYPE::PT_CENTER );
1446
1447 addAnchor( start + VECTOR2I( -r, 0 ), OUTLINE | SNAPPABLE, shape, POINT_TYPE::PT_QUADRANT );
1448 addAnchor( start + VECTOR2I( r, 0 ), OUTLINE | SNAPPABLE, shape, POINT_TYPE::PT_QUADRANT );
1449 addAnchor( start + VECTOR2I( 0, -r ), OUTLINE | SNAPPABLE, shape, POINT_TYPE::PT_QUADRANT );
1450 addAnchor( start + VECTOR2I( 0, r ), OUTLINE | SNAPPABLE, shape, POINT_TYPE::PT_QUADRANT );
1451 break;
1452 }
1453
1454 case SHAPE_T::ARC:
1455 addAnchor( shape->GetStart(), CORNER | SNAPPABLE, shape, POINT_TYPE::PT_END );
1456 addAnchor( shape->GetEnd(), CORNER | SNAPPABLE, shape, POINT_TYPE::PT_END );
1457 addAnchor( shape->GetArcMid(), CORNER | SNAPPABLE, shape, POINT_TYPE::PT_MID );
1459 break;
1460
1461 case SHAPE_T::RECTANGLE:
1462 {
1463 addRectPoints( BOX2I::ByCorners( start, end ), *shape );
1464 break;
1465 }
1466
1467 case SHAPE_T::SEGMENT:
1468 addAnchor( start, CORNER | SNAPPABLE, shape, POINT_TYPE::PT_END );
1470 addAnchor( shape->GetCenter(), CORNER | SNAPPABLE, shape, POINT_TYPE::PT_MID );
1471 break;
1472
1473 case SHAPE_T::POLY:
1474 {
1476 lc.SetClosed( true );
1477 for( const VECTOR2I& p : shape->GetPolyPoints() )
1478 {
1480 lc.Append( p );
1481 }
1482
1483 addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem );
1484 break;
1485 }
1486
1487 case SHAPE_T::BEZIER:
1488 addAnchor( start, CORNER | SNAPPABLE, shape, POINT_TYPE::PT_END );
1491
1492 default:
1493 addAnchor( shape->GetPosition(), ORIGIN | SNAPPABLE, shape );
1494 break;
1495 }
1496 };
1497
1498 switch( aItem->Type() )
1499 {
1500 case PCB_FOOTPRINT_T:
1501 {
1502 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem );
1503 bool footprintVisible = checkVisibility( footprint );
1504
1505 for( PAD* pad : footprint->Pads() )
1506 {
1507 if( aFrom )
1508 {
1509 if( aSelectionFilter && !aSelectionFilter->pads )
1510 continue;
1511 }
1512 else
1513 {
1515 continue;
1516 }
1517
1518 if( !checkVisibility( pad ) )
1519 continue;
1520
1521 if( !pad->GetBoundingBox().Contains( aRefPos ) )
1522 continue;
1523
1524 pad->Padstack().ForEachUniqueLayer(
1525 [&]( PCB_LAYER_ID aLayer )
1526 {
1527 if( !isHighContrast
1528 || PadstackUniqueLayerAppliesToLayer( pad->Padstack(), aLayer,
1529 activeHighContrastPrimaryLayer ) )
1530 {
1531 handlePadShape( pad, aLayer );
1532 }
1533 } );
1534 }
1535
1536 // Points are also pick-up points
1537 for( const PCB_POINT* pt : footprint->Points() )
1538 {
1539 if( aSelectionFilter && !aSelectionFilter->points )
1540 continue;
1541
1542 if( !checkVisibility( pt ) )
1543 continue;
1544
1545 addAnchor( pt->GetPosition(), ORIGIN | SNAPPABLE, footprint, POINT_TYPE::PT_CENTER );
1546 }
1547
1548 if( !footprintVisible )
1549 break;
1550
1551 if( aFrom && aSelectionFilter && !aSelectionFilter->footprints )
1552 break;
1553
1554 // If the cursor is not over a pad, snap to the anchor (if visible) or the center
1555 // (if markedly different from the anchor).
1556 VECTOR2I position = footprint->GetPosition();
1557 VECTOR2I center = footprint->GetBoundingBox( false ).Centre();
1558 VECTOR2I grid( GetGrid() );
1559
1560 // Don't snap to invisible anchors, which may be invisible because anchors are off,
1561 // or the footprint is on a layer not currently visible.
1562 if( view->IsLayerVisible( LAYER_ANCHOR )
1563 && footprint->ViewGetLOD( LAYER_ANCHOR, view ) < view->GetScale() )
1564 {
1565 addAnchor( position, ORIGIN | SNAPPABLE, footprint, POINT_TYPE::PT_CENTER );
1566 }
1567
1568 if( ( center - position ).SquaredEuclideanNorm() > grid.SquaredEuclideanNorm() )
1570
1571 break;
1572 }
1573
1574 case PCB_PAD_T:
1575 if( aFrom )
1576 {
1577 if( aSelectionFilter && !aSelectionFilter->pads )
1578 break;
1579 }
1580 else
1581 {
1583 break;
1584 }
1585
1586 if( checkVisibility( aItem ) )
1587 {
1588 PAD* pad = static_cast<PAD*>( aItem );
1589
1590 pad->Padstack().ForEachUniqueLayer(
1591 [&]( PCB_LAYER_ID aLayer )
1592 {
1593 if( !isHighContrast
1594 || PadstackUniqueLayerAppliesToLayer( pad->Padstack(), aLayer,
1595 activeHighContrastPrimaryLayer ) )
1596 {
1597 handlePadShape( pad, aLayer );
1598 }
1599 } );
1600 }
1601
1602 break;
1603
1604 case PCB_TEXTBOX_T:
1605 if( aFrom )
1606 {
1607 if( aSelectionFilter && !aSelectionFilter->text )
1608 break;
1609 }
1610 else
1611 {
1612 if( !m_magneticSettings->graphics )
1613 break;
1614 }
1615
1616 if( checkVisibility( aItem ) )
1617 handleShape( static_cast<PCB_SHAPE*>( aItem ) );
1618
1619 break;
1620
1621 case PCB_TABLE_T:
1622 if( aFrom )
1623 {
1624 if( aSelectionFilter && !aSelectionFilter->text )
1625 break;
1626 }
1627 else
1628 {
1629 if( !m_magneticSettings->graphics )
1630 break;
1631 }
1632
1633 if( checkVisibility( aItem ) )
1634 {
1635 PCB_TABLE* table = static_cast<PCB_TABLE*>( aItem );
1636
1637 EDA_ANGLE drawAngle = table->GetCell( 0, 0 )->GetDrawRotation();
1638 VECTOR2I topLeft = table->GetCell( 0, 0 )->GetCornersInSequence( drawAngle )[0];
1639 VECTOR2I bottomLeft =
1640 table->GetCell( table->GetRowCount() - 1, 0 )->GetCornersInSequence( drawAngle )[3];
1641 VECTOR2I topRight = table->GetCell( 0, table->GetColCount() - 1 )->GetCornersInSequence( drawAngle )[1];
1642 VECTOR2I bottomRight = table->GetCell( table->GetRowCount() - 1, table->GetColCount() - 1 )
1643 ->GetCornersInSequence( drawAngle )[2];
1644
1649
1650 addAnchor( table->GetCenter(), ORIGIN, table, POINT_TYPE::PT_MID );
1651 }
1652
1653 break;
1654
1655 case PCB_SHAPE_T:
1656 if( aFrom )
1657 {
1658 if( aSelectionFilter && !aSelectionFilter->graphics )
1659 break;
1660 }
1661 else
1662 {
1663 if( !m_magneticSettings->graphics )
1664 break;
1665 }
1666
1667 if( checkVisibility( aItem ) )
1668 handleShape( static_cast<PCB_SHAPE*>( aItem ) );
1669
1670 break;
1671
1672 case PCB_TRACE_T:
1673 case PCB_ARC_T:
1674 if( aFrom )
1675 {
1676 if( aSelectionFilter && !aSelectionFilter->tracks )
1677 break;
1678 }
1679 else
1680 {
1682 break;
1683 }
1684
1685 if( checkVisibility( aItem ) )
1686 {
1687 PCB_TRACK* track = static_cast<PCB_TRACK*>( aItem );
1688
1689 addAnchor( track->GetStart(), CORNER | SNAPPABLE, track, POINT_TYPE::PT_END );
1690 addAnchor( track->GetEnd(), CORNER | SNAPPABLE, track, POINT_TYPE::PT_END );
1691 addAnchor( track->GetCenter(), ORIGIN, track, POINT_TYPE::PT_MID );
1692 }
1693
1694 break;
1695
1696 case PCB_MARKER_T:
1697 case PCB_TARGET_T:
1699 break;
1700
1701 case PCB_POINT_T:
1702 if( aSelectionFilter && !aSelectionFilter->points )
1703 break;
1704
1705 if( checkVisibility( aItem ) )
1707
1708 break;
1709
1710 case PCB_VIA_T:
1711 if( aFrom )
1712 {
1713 if( aSelectionFilter && !aSelectionFilter->vias )
1714 break;
1715 }
1716 else
1717 {
1719 break;
1720 }
1721
1722 if( checkVisibility( aItem ) )
1724
1725 break;
1726
1727 case PCB_ZONE_T:
1728 if( aFrom && aSelectionFilter && !aSelectionFilter->zones )
1729 break;
1730
1731 if( checkVisibility( aItem ) )
1732 {
1733 const SHAPE_POLY_SET* outline = static_cast<const ZONE*>( aItem )->Outline();
1734
1736 lc.SetClosed( true );
1737
1738 for( auto iter = outline->CIterateWithHoles(); iter; iter++ )
1739 {
1740 addAnchor( *iter, CORNER | SNAPPABLE, aItem, POINT_TYPE::PT_CORNER );
1741 lc.Append( *iter );
1742 }
1743
1744 addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem );
1745 }
1746
1747 break;
1748
1749 case PCB_DIM_ALIGNED_T:
1751 if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
1752 break;
1753
1754 if( checkVisibility( aItem ) )
1755 {
1756 const PCB_DIM_ALIGNED* dim = static_cast<const PCB_DIM_ALIGNED*>( aItem );
1757 addAnchor( dim->GetCrossbarStart(), CORNER | SNAPPABLE, aItem );
1758 addAnchor( dim->GetCrossbarEnd(), CORNER | SNAPPABLE, aItem );
1759 addAnchor( dim->GetStart(), CORNER | SNAPPABLE, aItem );
1760 addAnchor( dim->GetEnd(), CORNER | SNAPPABLE, aItem );
1761 }
1762
1763 break;
1764
1765 case PCB_DIM_CENTER_T:
1766 if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
1767 break;
1768
1769 if( checkVisibility( aItem ) )
1770 {
1771 const PCB_DIM_CENTER* dim = static_cast<const PCB_DIM_CENTER*>( aItem );
1772 addAnchor( dim->GetStart(), CORNER | SNAPPABLE, aItem );
1773 addAnchor( dim->GetEnd(), CORNER | SNAPPABLE, aItem );
1774
1775 VECTOR2I start( dim->GetStart() );
1776 VECTOR2I radial( dim->GetEnd() - dim->GetStart() );
1777
1778 for( int i = 0; i < 2; i++ )
1779 {
1780 RotatePoint( radial, -ANGLE_90 );
1781 addAnchor( start + radial, CORNER | SNAPPABLE, aItem );
1782 }
1783 }
1784
1785 break;
1786
1787 case PCB_DIM_RADIAL_T:
1788 if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
1789 break;
1790
1791 if( checkVisibility( aItem ) )
1792 {
1793 const PCB_DIM_RADIAL* radialDim = static_cast<const PCB_DIM_RADIAL*>( aItem );
1794 addAnchor( radialDim->GetStart(), CORNER | SNAPPABLE, aItem );
1795 addAnchor( radialDim->GetEnd(), CORNER | SNAPPABLE, aItem );
1796 addAnchor( radialDim->GetKnee(), CORNER | SNAPPABLE, aItem );
1797 addAnchor( radialDim->GetTextPos(), CORNER | SNAPPABLE, aItem );
1798 }
1799
1800 break;
1801
1802 case PCB_DIM_LEADER_T:
1803 if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
1804 break;
1805
1806 if( checkVisibility( aItem ) )
1807 {
1808 const PCB_DIM_LEADER* leader = static_cast<const PCB_DIM_LEADER*>( aItem );
1809 addAnchor( leader->GetStart(), CORNER | SNAPPABLE, aItem );
1810 addAnchor( leader->GetEnd(), CORNER | SNAPPABLE, aItem );
1811 addAnchor( leader->GetTextPos(), CORNER | SNAPPABLE, aItem );
1812 }
1813
1814 break;
1815
1816 case PCB_FIELD_T:
1817 case PCB_TEXT_T:
1818 if( aFrom && aSelectionFilter && !aSelectionFilter->text )
1819 break;
1820
1821 if( checkVisibility( aItem ) )
1822 addAnchor( aItem->GetPosition(), ORIGIN, aItem );
1823
1824 break;
1825
1826 case PCB_GROUP_T:
1827 for( BOARD_ITEM* item : static_cast<PCB_GROUP*>( aItem )->GetBoardItems() )
1828 {
1829 if( checkVisibility( item ) )
1830 computeAnchors( item, aRefPos, aFrom, nullptr );
1831 }
1832
1833 break;
1834
1836 if( aFrom && aSelectionFilter && !aSelectionFilter->graphics )
1837 break;
1838
1839 if( checkVisibility( aItem ) )
1840 {
1841 const PCB_REFERENCE_IMAGE& image = static_cast<const PCB_REFERENCE_IMAGE&>( *aItem );
1842 const REFERENCE_IMAGE& refImg = image.GetReferenceImage();
1843 const BOX2I bbox = refImg.GetBoundingBox();
1844
1845 addRectPoints( bbox, *aItem );
1846
1847 if( refImg.GetTransformOriginOffset() != VECTOR2I( 0, 0 ) )
1848 {
1850 aItem, POINT_TYPE::PT_CENTER );
1851 }
1852 }
1853
1854 break;
1855
1856 default:
1857 break;
1858 }
1859}
1860
1861
1863{
1864 // Do this all in squared distances as we only care about relative distances
1866
1867 ecoord minDist = std::numeric_limits<ecoord>::max();
1868 std::vector<ANCHOR*> anchorsAtMinDistance;
1869
1870 for( ANCHOR& anchor : m_anchors )
1871 {
1872 // There is no need to filter by layers here, as the items are already filtered
1873 // by layer (if needed) when the anchors are computed.
1874 if( ( aFlags & anchor.flags ) != aFlags )
1875 continue;
1876
1877 if( !anchorsAtMinDistance.empty() && anchor.pos == anchorsAtMinDistance.front()->pos )
1878 {
1879 // Same distance as the previous best anchor
1880 anchorsAtMinDistance.push_back( &anchor );
1881 }
1882 else
1883 {
1884 const double dist = anchor.pos.SquaredDistance( aPos );
1885
1886 if( dist < minDist )
1887 {
1888 // New minimum distance
1889 minDist = dist;
1890 anchorsAtMinDistance.clear();
1891 anchorsAtMinDistance.push_back( &anchor );
1892 }
1893 }
1894 }
1895
1896 // Check that any involved real items are 'active'
1897 // (i.e. the user has moused over a key point previously)
1898 // If any are not real (e.g. snap lines), they are allowed to be involved
1899 //
1900 // This is an area most likely to be controversial/need tuning,
1901 // as some users will think it's fiddly; without 'activation', others will
1902 // think the snaps are intrusive.
1903 SNAP_MANAGER& snapManager = getSnapManager();
1904 const auto noRealItemsInAnchorAreInvolved = [&]( ANCHOR* aAnchor ) -> bool
1905 {
1906 // If no extension snaps are enabled, don't inhibit
1907 static const bool haveExtensions = ADVANCED_CFG::GetCfg().m_EnableExtensionSnaps;
1908
1909 if( !haveExtensions )
1910 return false;
1911
1912 // If the anchor is not constructed, it may be involved (because it is one
1913 // of the nearest anchors). The items will only be activated later, but don't
1914 // discard the anchor yet.
1915 const bool anchorIsConstructed = aAnchor->flags & ANCHOR_FLAGS::CONSTRUCTED;
1916
1917 if( !anchorIsConstructed )
1918 return false;
1919
1920 bool allRealAreInvolved = snapManager.GetConstructionManager().InvolvesAllGivenRealItems( aAnchor->items );
1921 return !allRealAreInvolved;
1922 };
1923
1924 // Trim out items that aren't involved
1925 std::erase_if( anchorsAtMinDistance, noRealItemsInAnchorAreInvolved );
1926
1927 // More than one anchor can be at the same distance, for example
1928 // two lines end-to-end each have the same endpoint anchor.
1929 // So, check which one has an involved item that's closest to the origin,
1930 // and use that one (which allows the user to choose which items
1931 // gets extended - it's the one nearest the cursor)
1932 ecoord minDistToItem = std::numeric_limits<ecoord>::max();
1933 ANCHOR* best = nullptr;
1934
1935 // One of the anchors at the minimum distance
1936 for( ANCHOR* const anchor : anchorsAtMinDistance )
1937 {
1938 ecoord distToNearestItem = std::numeric_limits<ecoord>::max();
1939
1940 for( EDA_ITEM* const item : anchor->items )
1941 {
1942 if( !item || !item->IsBOARD_ITEM() )
1943 continue;
1944
1945 std::optional<ecoord> distToThisItem =
1946 FindSquareDistanceToItem( static_cast<const BOARD_ITEM&>( *item ), aPos );
1947
1948 if( distToThisItem )
1949 distToNearestItem = std::min( distToNearestItem, *distToThisItem );
1950 }
1951
1952 // If the item doesn't have any special min-dist handler,
1953 // just use the distance to the anchor
1954 distToNearestItem = std::min( distToNearestItem, minDist );
1955
1956 if( distToNearestItem < minDistToItem )
1957 {
1958 minDistToItem = distToNearestItem;
1959 best = anchor;
1960 }
1961 }
1962
1963 return best;
1964}
@ ERROR_INSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
constexpr BOX2I BOX2ISafe(const BOX2D &aInput)
Definition box2.h:929
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
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:83
virtual VECTOR2I GetCenter() const
This defaults to the center of the bounding box if not overridden.
Definition board_item.h:116
FOOTPRINT * GetParentFootprint() const
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:256
virtual void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const
Invoke a function on all children.
Definition board_item.h:212
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
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:3006
void RemoveListener(BOARD_LISTENER *aListener)
Remove the specified listener.
Definition board.cpp:3013
static constexpr BOX2< VECTOR2I > ByCorners(const VECTOR2I &aCorner1, const VECTOR2I &aCorner2)
Definition box2.h:70
constexpr const Vec GetEnd() const
Definition box2.h:212
constexpr Vec Centre() const
Definition box2.h:97
constexpr const Vec GetCenter() const
Definition box2.h:230
constexpr coord_type GetLeft() const
Definition box2.h:228
constexpr const Vec & GetOrigin() const
Definition box2.h:210
constexpr coord_type GetRight() const
Definition box2.h:217
constexpr coord_type GetTop() const
Definition box2.h:229
constexpr coord_type GetBottom() const
Definition box2.h:222
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.
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...
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:272
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:233
bool IsMoving() const
Definition eda_item.h:125
std::vector< VECTOR2I > GetPolyPoints() const
Duplicate the polygon outlines into a flat list of VECTOR2I points.
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:168
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:215
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:173
VECTOR2I GetArcMid() const
const VECTOR2I & GetTextPos() const
Definition eda_text.h:273
PCB_POINTS & Points()
Definition footprint.h:236
double ViewGetLOD(int aLayer, const KIGFX::VIEW *aView) const override
Return the level of detail (LOD) of the item.
std::deque< PAD * > & Pads()
Definition footprint.h:224
VECTOR2I GetPosition() const override
Definition footprint.h:245
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
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
void showConstructionGeometry(bool aShow)
TOOL_MANAGER * m_toolMgr
VECTOR2D GetVisibleGrid() const
VECTOR2I GetGrid() const
bool m_enableSnapLine
bool m_enableSnap
VECTOR2I GetOrigin() const
void clearAnchors()
std::optional< ANCHOR > m_snapItem
KIGFX::ANCHOR_DEBUG * enableAndGetAnchorDebug()
Enable the anchor debug if permitted and return it.
KIGFX::SNAP_INDICATOR m_viewSnapPoint
void updateSnapPoint(const TYPED_POINT2I &aPoint)
KIGFX::ORIGIN_VIEWITEM m_viewAxis
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:105
COLOR4D WithAlpha(double aAlpha) const
Return a color with the same color, but the given alpha.
Definition color4d.h:312
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:102
virtual double ViewGetLOD(int aLayer, const VIEW *aView) const
Return the level of detail (LOD) of the item.
Definition view_item.h:155
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:66
double GetScale() const
Definition view.h:276
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition view.cpp:298
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition view.cpp:341
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:420
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition view.h:422
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition view.h:220
bool IsVisible(const VIEW_ITEM *aItem) const
Return information if the item is visible (or not).
Definition view.cpp:1655
void SetVisible(VIEW_ITEM *aItem, bool aIsVisible=true)
Set the item visibility.
Definition view.cpp:1612
Definition line.h:36
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllLayersMask()
Definition lset.cpp:624
A PADSTACK defines the characteristics of a single or multi-layer pad, in the IPC sense of the word.
Definition padstack.h:157
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:171
@ CUSTOM
Shapes can be defined on arbitrary layers.
Definition padstack.h:173
@ FRONT_INNER_BACK
Up to three shapes can be defined (F_Cu, inner copper layers, B_Cu)
Definition padstack.h:172
MODE Mode() const
Definition padstack.h:322
static constexpr PCB_LAYER_ID INNER_LAYERS
! The layer identifier to use for "inner layers" on top/inner/bottom padstacks
Definition padstack.h:180
Definition pad.h:55
int GetSizeX() const
Definition pad.h:280
const VECTOR2I & GetDrillSize() const
Definition pad.h:312
const VECTOR2I & GetDelta(PCB_LAYER_ID aLayer) const
Definition pad.h:299
VECTOR2I GetPosition() const override
Definition pad.h:209
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:196
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition pad.h:415
const std::shared_ptr< SHAPE_POLY_SET > & GetEffectivePolygon(PCB_LAYER_ID aLayer, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Definition pad.cpp:919
bool HasHole() const override
Definition pad.h:107
VECTOR2I ShapePos(PCB_LAYER_ID aLayer) const
Definition pad.cpp:1503
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
Definition pad.h:264
const VECTOR2I & GetMid() const
Definition pcb_track.h:347
virtual const VECTOR2I & GetStart() const
The dimension's origin is the first feature point for the dimension.
virtual const VECTOR2I & GetEnd() const
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
std::vector< NEARABLE_GEOM > m_pointOnLineCandidates
~PCB_GRID_HELPER() override
VECTOR2I AlignToArc(const VECTOR2I &aPoint, const SHAPE_ARC &aSeg)
VECTOR2I SnapToPad(const VECTOR2I &aMousePos, std::deque< PAD * > &aPads)
BOARD_ITEM * GetSnapped() const
Function GetSnapped If the PCB_GRID_HELPER has highlighted a snap point (target shown),...
VECTOR2D GetGridSize(GRID_HELPER_GRIDS aGrid) const override
Return the size of the specified grid.
VECTOR2I BestSnapAnchor(const VECTOR2I &aOrigin, BOARD_ITEM *aReferenceItem, GRID_HELPER_GRIDS aGrid=GRID_HELPER_GRIDS::GRID_CURRENT)
Chooses the "best" snap anchor around the given point, optionally taking layers from the reference it...
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)
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.
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)
virtual VECTOR2I Align(const VECTOR2I &aPoint, GRID_HELPER_GRIDS aGrid) const
Definition grid_helper.h:86
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::vector< BOARD_ITEM * > queryVisible(const BOX2I &aArea, const std::vector< BOARD_ITEM * > &aSkip) const
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:53
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:43
Object to handle a bitmap image that can be inserted in a PCB.
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
REFERENCE_IMAGE & GetReferenceImage()
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_shape.h:81
VECTOR2I GetPosition() const override
Definition pcb_shape.h:79
bool IsFootprintEditor() const
const VECTOR2I & GetStart() const
Definition pcb_track.h:154
const VECTOR2I & GetEnd() const
Definition pcb_track.h:151
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:42
VECTOR2I A
Definition seg.h:49
ecoord SquaredDistance(const SEG &aSeg) const
Definition seg.cpp:80
VECTOR2I::extended_type ecoord
Definition seg.h:44
VECTOR2I B
Definition seg.h:50
VECTOR2I Center() const
Definition seg.h:379
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition seg.h:220
const VECTOR2I & GetP1() const
Definition shape_arc.h:119
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:118
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
static SHAPE_SEGMENT BySizeAndCenter(const VECTOR2I &aSize, const VECTOR2I &aCenter, const EDA_ANGLE &aRotation)
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 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...
Master controller class:
EDA_ITEM * GetModel() const
Define a general 2D-vector/point.
Definition vector2d.h:71
constexpr extended_type SquaredDistance(const VECTOR2< T > &aVector) const
Compute the squared distance between two vectors.
Definition vector2d.h:569
static constexpr extended_type ECOORD_MAX
Definition vector2d.h:76
VECTOR2_TRAITS< int32_t >::extended_type extended_type
Definition vector2d.h:73
Handle a list of polygons defining a copper zone.
Definition zone.h:74
A type-safe container of any type.
Definition ki_any.h:93
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
@ RECURSE
Definition eda_item.h:51
@ SEGMENT
Definition eda_shape.h:45
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:46
GRID_HELPER_GRIDS
Definition grid_helper.h:44
@ GRID_VIAS
Definition grid_helper.h:50
@ GRID_TEXT
Definition grid_helper.h:51
@ GRID_CURRENT
Definition grid_helper.h:46
@ GRID_GRAPHICS
Definition grid_helper.h:52
@ GRID_CONNECTABLE
Definition grid_helper.h:48
@ GRID_WIRES
Definition grid_helper.h:49
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, 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:666
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:677
@ LAYER_AUX_ITEMS
Auxiliary items (guides, rule, etc).
Definition layer_ids.h:283
@ LAYER_ANCHOR
Anchor of items having an anchor point (texts, footprints).
Definition layer_ids.h:248
bool IsInnerCopperLayer(int aLayerId)
Test whether a layer is an inner (In1_Cu to In30_Cu) copper layer.
Definition layer_ids.h:699
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ B_Cu
Definition layer_ids.h:65
@ F_Cu
Definition layer_ids.h:64
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:83
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:50
std::array< SEG, 4 > BoxToSegs(const BOX2I &aBox)
Decompose a BOX2 into four segments.
@ OVAL_CAP_TIPS
Definition oval.h:49
@ OVAL_SIDE_MIDPOINTS
Definition oval.h:51
@ OVAL_CARDINAL_EXTREMES
Definition oval.h:53
@ OVAL_CENTER
Definition oval.h:48
unsigned int OVAL_KEY_POINT_FLAGS
Definition oval.h:57
@ GEOMETRY
Position or shape has changed.
Definition view_item.h:55
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:58
std::variant< LINE, HALF_LINE, SEG, CIRCLE, SHAPE_ARC, BOX2I, VECTOR2I > NEARABLE_GEOM
A variant type that can hold any of the supported geometry types for nearest point calculations.
Definition nearest.h:43
@ CHAMFERED_RECT
Definition padstack.h:60
@ ROUNDRECT
Definition padstack.h:57
@ TRAPEZOID
Definition padstack.h:56
@ RECTANGLE
Definition padstack.h:54
static bool PadstackUniqueLayerAppliesToLayer(const PADSTACK &aPadStack, PCB_LAYER_ID aPadstackUniqueLayer, const PCB_LAYER_ID aRealLayer)
Class to handle a set of BOARD_ITEMs.
@ PT_INTERSECTION
The point is an intersection of two (or more) items.
Definition point_types.h:67
@ PT_CENTER
The point is the center of something.
Definition point_types.h:46
@ 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:63
@ PT_NONE
No specific point type.
Definition point_types.h:42
@ PT_QUADRANT
The point is on a quadrant of a circle (N, E, S, W points).
Definition point_types.h:58
@ PT_END
The point is at the end of a segment, arc, etc.
Definition point_types.h:50
@ PT_MID
The point is at the middle of a segment, arc, etc.
Definition point_types.h:54
@ PT_ON_ELEMENT
The point is somewhere on another element, but not some specific point.
Definition point_types.h:72
std::optional< VECTOR2I > OPT_VECTOR2I
Definition seg.h:39
VECTOR2I::extended_type ecoord
Utility functions for working with shapes.
KIGFX::CONSTRUCTION_GEOM::DRAWABLE Drawable
Items to be used for the construction of "virtual" anchors, for example, when snapping to a point inv...
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
A visitor that visits INTERSECTABLE_GEOM variant objects with another (which is held as state: m_othe...
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 graphics
Graphic lines, shapes, polygons.
bool footprints
Allow selecting entire footprints.
bool text
Text (free or attached to a footprint)
VECTOR2I center
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
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:229
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:88
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:106
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:103
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:97
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:104
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:111
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:93
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:108
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:92
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:89
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:90
@ PCB_MARKER_T
class PCB_MARKER, a marker used to show something
Definition typeinfo.h:99
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:101
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition typeinfo.h:107
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:86
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:102
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:98
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:100
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:94
@ PCB_POINT_T
class PCB_POINT, a 0-dimensional point
Definition typeinfo.h:113
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:96
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:105
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
VECTOR2< double > VECTOR2D
Definition vector2d.h:694