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