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