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