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