KiCad PCB EDA Suite
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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 <tomasz.wlostowski@cern.ch>
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->RunOnChildren(
915 [&]( BOARD_ITEM* aChild )
916 {
917 skipItem( aChild );
918 },
919 RECURSE_MODE::RECURSE );
920 };
921
922 for( BOARD_ITEM* item : aSkip )
923 skipItem( item );
924
925 return {items.begin(), items.end()};
926}
927
928
930{
933
934 // Clang wants this constructor
936 Item( aItem ), Geometry( std::move( aSeg ) )
937 {
938 }
939};
940
941
942void PCB_GRID_HELPER::computeAnchors( const std::vector<BOARD_ITEM*>& aItems,
943 const VECTOR2I& aRefPos, bool aFrom,
944 const PCB_SELECTION_FILTER_OPTIONS* aSelectionFilter,
945 const LSET* aMatchLayers, bool aForDrag )
946{
947 std::vector<PCB_INTERSECTABLE> intersectables;
948
949 // These could come from a more granular snap mode filter
950 // But when looking for drag points, we don't want construction geometry
951 const bool computeIntersections = !aForDrag;
952 const bool computePointsOnElements = !aForDrag;
953 const bool excludeGraphics = aSelectionFilter && !aSelectionFilter->graphics;
954 const bool excludeTracks = aSelectionFilter && !aSelectionFilter->tracks;
955
956 const auto itemIsSnappable = [&]( const BOARD_ITEM& aItem )
957 {
958 // If we are filtering by layers, check if the item matches
959 if( aMatchLayers )
960 {
962 || ( ( *aMatchLayers & aItem.GetLayerSet() ).any() );
963 }
964 return true;
965 };
966
967 const auto processItem = [&]( BOARD_ITEM& item )
968 {
969 // Don't even process the item if it doesn't match the layers
970 if( !itemIsSnappable( item ) )
971 {
972 return;
973 }
974
975 // First, add all the key points of the item itself
976 computeAnchors( &item, aRefPos, aFrom, aSelectionFilter );
977
978 // If we are computing intersections, construct the relevant intersectables
979 // Points on elements also use the intersectables.
980 if( computeIntersections || computePointsOnElements )
981 {
982 std::optional<INTERSECTABLE_GEOM> intersectableGeom;
983 if( !excludeGraphics
984 && ( item.Type() == PCB_SHAPE_T || item.Type() == PCB_REFERENCE_IMAGE_T ) )
985 {
986 intersectableGeom = GetBoardIntersectable( item );
987 }
988 else if( !excludeTracks && ( item.Type() == PCB_TRACE_T || item.Type() == PCB_ARC_T ) )
989 {
990 intersectableGeom = GetBoardIntersectable( item );
991 }
992
993 if( intersectableGeom )
994 {
995 intersectables.emplace_back( &item, *intersectableGeom );
996 }
997 }
998 };
999
1000 for( BOARD_ITEM* item : aItems )
1001 {
1002 processItem( *item );
1003 }
1004
1006 getSnapManager().GetConstructionItems() )
1007 {
1008 for( const CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM& constructionItem : batch )
1009 {
1010 BOARD_ITEM* involvedItem = static_cast<BOARD_ITEM*>( constructionItem.Item );
1011
1012
1013 for( const KIGFX::CONSTRUCTION_GEOM::DRAWABLE& drawable :
1014 constructionItem.Constructions )
1015 {
1016 std::visit(
1017 [&]( const auto& visited )
1018 {
1019 using ItemType = std::decay_t<decltype( visited )>;
1020
1021 if constexpr( std::is_same_v<ItemType, LINE>
1022 || std::is_same_v<ItemType, CIRCLE>
1023 || std::is_same_v<ItemType, HALF_LINE>
1024 || std::is_same_v<ItemType, SHAPE_ARC> )
1025 {
1026 intersectables.emplace_back( involvedItem, visited );
1027 }
1028 else if constexpr( std::is_same_v<ItemType, VECTOR2I> )
1029 {
1030 // Add any free-floating points as snap points.
1031 addAnchor( visited, SNAPPABLE | CONSTRUCTED, involvedItem,
1032 POINT_TYPE::PT_NONE );
1033 }
1034 },
1035 drawable );
1036 }
1037 }
1038 }
1039
1040 // Now, add all the intersections between the items
1041 // This is obviously quadratic, so performance may be a concern for large selections
1042 // But, so far up to ~20k comparisons seems not to be an issue with run times in the ms range
1043 // and it's usually only a handful of items.
1044
1045 if( computeIntersections )
1046 {
1047 for( std::size_t ii = 0; ii < intersectables.size(); ++ii )
1048 {
1049 const PCB_INTERSECTABLE& intersectableA = intersectables[ii];
1050
1051 for( std::size_t jj = ii + 1; jj < intersectables.size(); ++jj )
1052 {
1053 const PCB_INTERSECTABLE& intersectableB = intersectables[jj];
1054
1055 // An item and its own extension will often have intersections (as they are on top of each other),
1056 // but they not useful points to snap to
1057 if( intersectableA.Item == intersectableB.Item )
1058 continue;
1059
1060 std::vector<VECTOR2I> intersections;
1061 const INTERSECTION_VISITOR visitor{ intersectableA.Geometry, intersections };
1062
1063 std::visit( visitor, intersectableB.Geometry );
1064
1065 // For each intersection, add an intersection snap anchor
1066 for( const VECTOR2I& intersection : intersections )
1067 {
1068 std::vector<EDA_ITEM*> items = {
1069 intersectableA.Item,
1070 intersectableB.Item,
1071 };
1072 addAnchor( intersection, SNAPPABLE | CONSTRUCTED, std::move( items ),
1073 POINT_TYPE::PT_INTERSECTION );
1074 }
1075 }
1076 }
1077 }
1078
1079 // The intersectables can also be used for fall-back snapping to "point on line"
1080 // snaps if no other snap is found
1082 if( computePointsOnElements )
1083 {
1084 // For the moment, it's trivial to make a NEARABLE from an INTERSECTABLE,
1085 // because all INTERSECTABLEs are also NEARABLEs.
1086 for( const PCB_INTERSECTABLE& intersectable : intersectables )
1087 {
1088 std::visit(
1089 [&]( const auto& geom )
1090 {
1091 NEARABLE_GEOM nearable( geom );
1092 m_pointOnLineCandidates.emplace_back( nearable );
1093 },
1094 intersectable.Geometry );
1095 }
1096 }
1097}
1098
1099
1100void PCB_GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos, bool aFrom,
1101 const PCB_SELECTION_FILTER_OPTIONS* aSelectionFilter )
1102{
1103 KIGFX::VIEW* view = m_toolMgr->GetView();
1104 RENDER_SETTINGS* settings = view->GetPainter()->GetSettings();
1105 const std::set<int>& activeLayers = settings->GetHighContrastLayers();
1106 bool isHighContrast = settings->GetHighContrast();
1107
1108 auto checkVisibility =
1109 [&]( BOARD_ITEM* item )
1110 {
1111 // New moved items don't yet have view flags so VIEW will call them invisible
1112 if( !view->IsVisible( item ) && !item->IsMoving() )
1113 return false;
1114
1115 bool onActiveLayer = !isHighContrast;
1116 bool isLODVisible = false;
1117
1118 for( PCB_LAYER_ID layer : item->GetLayerSet().Seq() )
1119 {
1120 if( !onActiveLayer && activeLayers.count( layer ) )
1121 onActiveLayer = true;
1122
1123 if( !isLODVisible && item->ViewGetLOD( layer, view ) < view->GetScale() )
1124 isLODVisible = true;
1125
1126 if( onActiveLayer && isLODVisible )
1127 return true;
1128 }
1129
1130 return false;
1131 };
1132
1133 // As defaults, these are probably reasonable to avoid spamming key points
1134 const KIGEOM::OVAL_KEY_POINT_FLAGS ovalKeyPointFlags =
1137
1138 auto handlePadShape = [&]( PAD* aPad, PCB_LAYER_ID aLayer )
1139 {
1140 addAnchor( aPad->GetPosition(), ORIGIN | SNAPPABLE, aPad, POINT_TYPE::PT_CENTER );
1141
1143 if( aFrom )
1144 return;
1145
1146 switch( aPad->GetShape( aLayer ) )
1147 {
1148 case PAD_SHAPE::CIRCLE:
1149 {
1150 const CIRCLE circle( aPad->ShapePos( aLayer ), aPad->GetSizeX() / 2 );
1151
1152 for( const TYPED_POINT2I& pt : KIGEOM::GetCircleKeyPoints( circle, false ) )
1153 {
1154 addAnchor( pt.m_point, OUTLINE | SNAPPABLE, aPad, pt.m_types );
1155 }
1156
1157 break;
1158 }
1159 case PAD_SHAPE::OVAL:
1160 {
1161 const OVAL oval( aPad->GetSize( aLayer ), aPad->GetPosition(), aPad->GetOrientation() );
1162
1163 for( const TYPED_POINT2I& pt : KIGEOM::GetOvalKeyPoints( oval, ovalKeyPointFlags ) )
1164 {
1165 addAnchor( pt.m_point, OUTLINE | SNAPPABLE, aPad, pt.m_types );
1166 }
1167
1168 break;
1169 }
1170 case PAD_SHAPE::RECTANGLE:
1171 case PAD_SHAPE::TRAPEZOID:
1172 case PAD_SHAPE::ROUNDRECT:
1173 case PAD_SHAPE::CHAMFERED_RECT:
1174 {
1175 VECTOR2I half_size( aPad->GetSize( aLayer ) / 2 );
1176 VECTOR2I trap_delta( 0, 0 );
1177
1178 if( aPad->GetShape( aLayer ) == PAD_SHAPE::TRAPEZOID )
1179 trap_delta = aPad->GetDelta( aLayer ) / 2;
1180
1181 SHAPE_LINE_CHAIN corners;
1182
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.Append( -half_size.x + trap_delta.y, -half_size.y - trap_delta.x );
1187 corners.SetClosed( true );
1188
1189 corners.Rotate( aPad->GetOrientation() );
1190 corners.Move( aPad->ShapePos( aLayer ) );
1191
1192 for( std::size_t ii = 0; ii < corners.GetSegmentCount(); ++ii )
1193 {
1194 const SEG& seg = corners.GetSegment( ii );
1195 addAnchor( seg.A, OUTLINE | SNAPPABLE, aPad, POINT_TYPE::PT_CORNER );
1196 addAnchor( seg.Center(), OUTLINE | SNAPPABLE, aPad, POINT_TYPE::PT_MID );
1197
1198 if( ii == corners.GetSegmentCount() - 1 )
1199 addAnchor( seg.B, OUTLINE | SNAPPABLE, aPad, POINT_TYPE::PT_CORNER );
1200 }
1201
1202 break;
1203 }
1204
1205 default:
1206 {
1207 const auto& outline = aPad->GetEffectivePolygon( aLayer, ERROR_INSIDE );
1208
1209 if( !outline->IsEmpty() )
1210 {
1211 for( const VECTOR2I& pt : outline->Outline( 0 ).CPoints() )
1212 addAnchor( pt, OUTLINE | SNAPPABLE, aPad );
1213 }
1214
1215 break;
1216 }
1217 }
1218
1219 if( aPad->HasHole() )
1220 {
1221 // Holes are at the pad centre (it's the shape that may be offset)
1222 const VECTOR2I hole_pos = aPad->GetPosition();
1223 const VECTOR2I hole_size = aPad->GetDrillSize();
1224
1225 std::vector<TYPED_POINT2I> snap_pts;
1226
1227 if( hole_size.x == hole_size.y )
1228 {
1229 // Circle
1230 const CIRCLE circle( hole_pos, hole_size.x / 2 );
1231 snap_pts = KIGEOM::GetCircleKeyPoints( circle, true );
1232 }
1233 else
1234 {
1235 // Oval
1236
1237 // For now there's no way to have an off-angle hole, so this is the
1238 // same as the pad. In future, this may not be true:
1239 // https://gitlab.com/kicad/code/kicad/-/issues/4124
1240 const OVAL oval( hole_size, hole_pos, aPad->GetOrientation() );
1241 snap_pts = KIGEOM::GetOvalKeyPoints( oval, ovalKeyPointFlags );
1242 }
1243
1244 for( const TYPED_POINT2I& snap_pt : snap_pts )
1245 addAnchor( snap_pt.m_point, OUTLINE | SNAPPABLE, aPad, snap_pt.m_types );
1246 }
1247 };
1248
1249 const auto addRectPoints = [&]( const BOX2I& aBox, EDA_ITEM& aRelatedItem )
1250 {
1251 const VECTOR2I topRight( aBox.GetRight(), aBox.GetTop() );
1252 const VECTOR2I bottomLeft( aBox.GetLeft(), aBox.GetBottom() );
1253
1254 const SEG first( aBox.GetOrigin(), topRight );
1255 const SEG second( topRight, aBox.GetEnd() );
1256 const SEG third( aBox.GetEnd(), bottomLeft );
1257 const SEG fourth( bottomLeft, aBox.GetOrigin() );
1258
1259 const int snapFlags = CORNER | SNAPPABLE;
1260
1261 addAnchor( aBox.GetCenter(), snapFlags, &aRelatedItem, POINT_TYPE::PT_CENTER );
1262
1263 addAnchor( first.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER );
1264 addAnchor( first.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID );
1265 addAnchor( second.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER );
1266 addAnchor( second.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID );
1267 addAnchor( third.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER );
1268 addAnchor( third.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID );
1269 addAnchor( fourth.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER );
1270 addAnchor( fourth.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID );
1271 };
1272
1273 const auto handleShape =
1274 [&]( PCB_SHAPE* shape )
1275 {
1276 VECTOR2I start = shape->GetStart();
1277 VECTOR2I end = shape->GetEnd();
1278
1279 switch( shape->GetShape() )
1280 {
1281 case SHAPE_T::CIRCLE:
1282 {
1283 const int r = ( start - end ).EuclideanNorm();
1284
1285 addAnchor( start, ORIGIN | SNAPPABLE, shape, POINT_TYPE::PT_CENTER );
1286
1287 addAnchor( start + VECTOR2I( -r, 0 ), OUTLINE | SNAPPABLE, shape,
1288 POINT_TYPE::PT_QUADRANT );
1289 addAnchor( start + VECTOR2I( r, 0 ), OUTLINE | SNAPPABLE, shape,
1290 POINT_TYPE::PT_QUADRANT );
1291 addAnchor( start + VECTOR2I( 0, -r ), OUTLINE | SNAPPABLE, shape,
1292 POINT_TYPE::PT_QUADRANT );
1293 addAnchor( start + VECTOR2I( 0, r ), OUTLINE | SNAPPABLE, shape,
1294 POINT_TYPE::PT_QUADRANT );
1295 break;
1296 }
1297
1298 case SHAPE_T::ARC:
1299 addAnchor( shape->GetStart(), CORNER | SNAPPABLE, shape,
1300 POINT_TYPE::PT_END );
1301 addAnchor( shape->GetEnd(), CORNER | SNAPPABLE, shape,
1302 POINT_TYPE::PT_END );
1303 addAnchor( shape->GetArcMid(), CORNER | SNAPPABLE, shape,
1304 POINT_TYPE::PT_MID );
1305 addAnchor( shape->GetCenter(), ORIGIN | SNAPPABLE, shape,
1306 POINT_TYPE::PT_CENTER );
1307 break;
1308
1309 case SHAPE_T::RECTANGLE:
1310 {
1311 addRectPoints( BOX2I::ByCorners( start, end ), *shape );
1312 break;
1313 }
1314
1315 case SHAPE_T::SEGMENT:
1316 addAnchor( start, CORNER | SNAPPABLE, shape, POINT_TYPE::PT_END );
1317 addAnchor( end, CORNER | SNAPPABLE, shape, POINT_TYPE::PT_END );
1318 addAnchor( shape->GetCenter(), CORNER | SNAPPABLE, shape,
1319 POINT_TYPE::PT_MID );
1320 break;
1321
1322 case SHAPE_T::POLY:
1323 {
1325 lc.SetClosed( true );
1326 std::vector<VECTOR2I> poly;
1327 shape->DupPolyPointsList( poly );
1328
1329 for( const VECTOR2I& p : poly )
1330 {
1331 addAnchor( p, CORNER | SNAPPABLE, shape, POINT_TYPE::PT_CORNER );
1332 lc.Append( p );
1333 }
1334
1335 addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem );
1336 break;
1337 }
1338
1339 case SHAPE_T::BEZIER:
1340 addAnchor( start, CORNER | SNAPPABLE, shape, POINT_TYPE::PT_END );
1341 addAnchor( end, CORNER | SNAPPABLE, shape, POINT_TYPE::PT_END );
1343
1344 default:
1345 addAnchor( shape->GetPosition(), ORIGIN | SNAPPABLE, shape );
1346 break;
1347 }
1348 };
1349
1350 switch( aItem->Type() )
1351 {
1352 case PCB_FOOTPRINT_T:
1353 {
1354 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem );
1355
1356 for( PAD* pad : footprint->Pads() )
1357 {
1358 if( aFrom )
1359 {
1360 if( aSelectionFilter && !aSelectionFilter->pads )
1361 continue;
1362 }
1363 else
1364 {
1365 if( m_magneticSettings->pads != MAGNETIC_OPTIONS::CAPTURE_ALWAYS )
1366 continue;
1367 }
1368
1369 if( !checkVisibility( pad ) )
1370 continue;
1371
1372 if( !pad->GetBoundingBox().Contains( aRefPos ) )
1373 continue;
1374
1375 pad->Padstack().ForEachUniqueLayer(
1376 [&]( PCB_LAYER_ID aLayer )
1377 {
1378 if( !isHighContrast || activeLayers.count( aLayer ) )
1379 handlePadShape( pad, aLayer );
1380 } );
1381 }
1382
1383 if( aFrom && aSelectionFilter && !aSelectionFilter->footprints )
1384 break;
1385
1386 // If the cursor is not over a pad, snap to the anchor (if visible) or the center
1387 // (if markedly different from the anchor).
1388 VECTOR2I position = footprint->GetPosition();
1389 VECTOR2I center = footprint->GetBoundingBox( false ).Centre();
1390 VECTOR2I grid( GetGrid() );
1391
1392 if( view->IsLayerVisible( LAYER_ANCHOR ) )
1393 addAnchor( position, ORIGIN | SNAPPABLE, footprint, POINT_TYPE::PT_CENTER );
1394
1395 if( ( center - position ).SquaredEuclideanNorm() > grid.SquaredEuclideanNorm() )
1396 addAnchor( center, ORIGIN | SNAPPABLE, footprint, POINT_TYPE::PT_CENTER );
1397
1398 break;
1399 }
1400
1401 case PCB_PAD_T:
1402 if( aFrom )
1403 {
1404 if( aSelectionFilter && !aSelectionFilter->pads )
1405 break;
1406 }
1407 else
1408 {
1409 if( m_magneticSettings->pads != MAGNETIC_OPTIONS::CAPTURE_ALWAYS )
1410 break;
1411 }
1412
1413 if( checkVisibility( aItem ) )
1414 {
1415 PAD* pad = static_cast<PAD*>( aItem );
1416
1417 pad->Padstack().ForEachUniqueLayer(
1418 [&]( PCB_LAYER_ID aLayer )
1419 {
1420 if( !isHighContrast || activeLayers.count( aLayer ) )
1421 handlePadShape( pad, aLayer );
1422 } );
1423 }
1424
1425 break;
1426
1427 case PCB_TEXTBOX_T:
1428 if( aFrom )
1429 {
1430 if( aSelectionFilter && !aSelectionFilter->text )
1431 break;
1432 }
1433 else
1434 {
1436 break;
1437 }
1438
1439 if( checkVisibility( aItem ) )
1440 handleShape( static_cast<PCB_SHAPE*>( aItem ) );
1441
1442 break;
1443
1444 case PCB_SHAPE_T:
1445 if( aFrom )
1446 {
1447 if( aSelectionFilter && !aSelectionFilter->graphics )
1448 break;
1449 }
1450 else
1451 {
1453 break;
1454 }
1455
1456 if( checkVisibility( aItem ) )
1457 handleShape( static_cast<PCB_SHAPE*>( aItem ) );
1458
1459 break;
1460
1461 case PCB_TRACE_T:
1462 case PCB_ARC_T:
1463 if( aFrom )
1464 {
1465 if( aSelectionFilter && !aSelectionFilter->tracks )
1466 break;
1467 }
1468 else
1469 {
1470 if( m_magneticSettings->tracks != MAGNETIC_OPTIONS::CAPTURE_ALWAYS )
1471 break;
1472 }
1473
1474 if( checkVisibility( aItem ) )
1475 {
1476 PCB_TRACK* track = static_cast<PCB_TRACK*>( aItem );
1477
1478 addAnchor( track->GetStart(), CORNER | SNAPPABLE, track, POINT_TYPE::PT_END );
1479 addAnchor( track->GetEnd(), CORNER | SNAPPABLE, track, POINT_TYPE::PT_END );
1480 addAnchor( track->GetCenter(), ORIGIN, track, POINT_TYPE::PT_MID );
1481 }
1482
1483 break;
1484
1485 case PCB_MARKER_T:
1486 case PCB_TARGET_T:
1487 addAnchor( aItem->GetPosition(), ORIGIN | CORNER | SNAPPABLE, aItem,
1488 POINT_TYPE::PT_CENTER );
1489 break;
1490
1491 case PCB_VIA_T:
1492 if( aFrom )
1493 {
1494 if( aSelectionFilter && !aSelectionFilter->vias )
1495 break;
1496 }
1497 else
1498 {
1499 if( m_magneticSettings->tracks != MAGNETIC_OPTIONS::CAPTURE_ALWAYS )
1500 break;
1501 }
1502
1503 if( checkVisibility( aItem ) )
1504 addAnchor( aItem->GetPosition(), ORIGIN | CORNER | SNAPPABLE, aItem,
1505 POINT_TYPE::PT_CENTER );
1506
1507 break;
1508
1509 case PCB_ZONE_T:
1510 if( aFrom && aSelectionFilter && !aSelectionFilter->zones )
1511 break;
1512
1513 if( checkVisibility( aItem ) )
1514 {
1515 const SHAPE_POLY_SET* outline = static_cast<const ZONE*>( aItem )->Outline();
1516
1518 lc.SetClosed( true );
1519
1520 for( auto iter = outline->CIterateWithHoles(); iter; iter++ )
1521 {
1522 addAnchor( *iter, CORNER | SNAPPABLE, aItem, POINT_TYPE::PT_CORNER );
1523 lc.Append( *iter );
1524 }
1525
1526 addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem );
1527 }
1528
1529 break;
1530
1531 case PCB_DIM_ALIGNED_T:
1533 if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
1534 break;
1535
1536 if( checkVisibility( aItem ) )
1537 {
1538 const PCB_DIM_ALIGNED* dim = static_cast<const PCB_DIM_ALIGNED*>( aItem );
1539 addAnchor( dim->GetCrossbarStart(), CORNER | SNAPPABLE, aItem );
1540 addAnchor( dim->GetCrossbarEnd(), CORNER | SNAPPABLE, aItem );
1541 addAnchor( dim->GetStart(), CORNER | SNAPPABLE, aItem );
1542 addAnchor( dim->GetEnd(), CORNER | SNAPPABLE, aItem );
1543 }
1544
1545 break;
1546
1547 case PCB_DIM_CENTER_T:
1548 if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
1549 break;
1550
1551 if( checkVisibility( aItem ) )
1552 {
1553 const PCB_DIM_CENTER* dim = static_cast<const PCB_DIM_CENTER*>( aItem );
1554 addAnchor( dim->GetStart(), CORNER | SNAPPABLE, aItem );
1555 addAnchor( dim->GetEnd(), CORNER | SNAPPABLE, aItem );
1556
1557 VECTOR2I start( dim->GetStart() );
1558 VECTOR2I radial( dim->GetEnd() - dim->GetStart() );
1559
1560 for( int i = 0; i < 2; i++ )
1561 {
1562 RotatePoint( radial, -ANGLE_90 );
1563 addAnchor( start + radial, CORNER | SNAPPABLE, aItem );
1564 }
1565 }
1566
1567 break;
1568
1569 case PCB_DIM_RADIAL_T:
1570 if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
1571 break;
1572
1573 if( checkVisibility( aItem ) )
1574 {
1575 const PCB_DIM_RADIAL* radialDim = static_cast<const PCB_DIM_RADIAL*>( aItem );
1576 addAnchor( radialDim->GetStart(), CORNER | SNAPPABLE, aItem );
1577 addAnchor( radialDim->GetEnd(), CORNER | SNAPPABLE, aItem );
1578 addAnchor( radialDim->GetKnee(), CORNER | SNAPPABLE, aItem );
1579 addAnchor( radialDim->GetTextPos(), CORNER | SNAPPABLE, aItem );
1580 }
1581
1582 break;
1583
1584 case PCB_DIM_LEADER_T:
1585 if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
1586 break;
1587
1588 if( checkVisibility( aItem ) )
1589 {
1590 const PCB_DIM_LEADER* leader = static_cast<const PCB_DIM_LEADER*>( aItem );
1591 addAnchor( leader->GetStart(), CORNER | SNAPPABLE, aItem );
1592 addAnchor( leader->GetEnd(), CORNER | SNAPPABLE, aItem );
1593 addAnchor( leader->GetTextPos(), CORNER | SNAPPABLE, aItem );
1594 }
1595
1596 break;
1597
1598 case PCB_FIELD_T:
1599 case PCB_TEXT_T:
1600 if( aFrom && aSelectionFilter && !aSelectionFilter->text )
1601 break;
1602
1603 if( checkVisibility( aItem ) )
1604 addAnchor( aItem->GetPosition(), ORIGIN, aItem );
1605
1606 break;
1607
1608 case PCB_GROUP_T:
1609 for( BOARD_ITEM* item : static_cast<const PCB_GROUP*>( aItem )->GetItems() )
1610 {
1611 if( checkVisibility( item ) )
1612 computeAnchors( item, aRefPos, aFrom, nullptr );
1613 }
1614
1615 break;
1616
1618 if( aFrom && aSelectionFilter && !aSelectionFilter->graphics )
1619 break;
1620
1621 if( checkVisibility( aItem ) )
1622 {
1623 const PCB_REFERENCE_IMAGE& image =
1624 static_cast<const PCB_REFERENCE_IMAGE&>( *aItem );
1625 const REFERENCE_IMAGE& refImg = image.GetReferenceImage();
1626 const BOX2I bbox = refImg.GetBoundingBox();
1627
1628 addRectPoints( bbox, *aItem );
1629
1630 if( refImg.GetTransformOriginOffset() != VECTOR2I( 0, 0 ) )
1631 {
1633 aItem, POINT_TYPE::PT_CENTER );
1634 }
1635 }
1636
1637 break;
1638
1639 default:
1640 break;
1641 }
1642}
1643
1644
1646{
1647 // Do this all in squared distances as we only care about relative distances
1649
1650 ecoord minDist = std::numeric_limits<ecoord>::max();
1651 std::vector<ANCHOR*> anchorsAtMinDistance;
1652
1653 for( ANCHOR& anchor : m_anchors )
1654 {
1655 // There is no need to filter by layers here, as the items are already filtered
1656 // by layer (if needed) when the anchors are computed.
1657 if( ( aFlags & anchor.flags ) != aFlags )
1658 continue;
1659
1660 if( !anchorsAtMinDistance.empty() && anchor.pos == anchorsAtMinDistance.front()->pos )
1661 {
1662 // Same distance as the previous best anchor
1663 anchorsAtMinDistance.push_back( &anchor );
1664 }
1665 else
1666 {
1667 const double dist = anchor.pos.SquaredDistance( aPos );
1668 if( dist < minDist )
1669 {
1670 // New minimum distance
1671 minDist = dist;
1672 anchorsAtMinDistance.clear();
1673 anchorsAtMinDistance.push_back( &anchor );
1674 }
1675 }
1676 }
1677
1678 // More than one anchor can be at the same distance, for example
1679 // two lines end-to-end each have the same endpoint anchor.
1680 // So, check which one has an involved item that's closest to the origin,
1681 // and use that one (which allows the user to choose which items
1682 // gets extended - it's the one nearest the cursor)
1683 ecoord minDistToItem = std::numeric_limits<ecoord>::max();
1684 ANCHOR* best = nullptr;
1685
1686 // One of the anchors at the minimum distance
1687 for( ANCHOR* const anchor : anchorsAtMinDistance )
1688 {
1689 ecoord distToNearestItem = std::numeric_limits<ecoord>::max();
1690 for( EDA_ITEM* const item : anchor->items )
1691 {
1692 if( !item )
1693 continue;
1694
1695 std::optional<ecoord> distToThisItem =
1696 FindSquareDistanceToItem( static_cast<const BOARD_ITEM&>( *item ), aPos );
1697
1698 if( distToThisItem )
1699 distToNearestItem = std::min( distToNearestItem, *distToThisItem );
1700 }
1701
1702 // If the item doesn't have any special min-dist handler,
1703 // just use the distance to the anchor
1704 distToNearestItem = std::min( distToNearestItem, minDist );
1705
1706 if( distToNearestItem < minDistToItem )
1707 {
1708 minDistToItem = distToNearestItem;
1709 best = anchor;
1710 }
1711 }
1712
1713 return best;
1714}
@ 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:213
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:78
virtual VECTOR2I GetCenter() const
This defaults to the center of the bounding box if not overridden.
Definition: board_item.h:115
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:305
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:255
virtual void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const
Invoke a function on all children.
Definition: board_item.h:211
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:95
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:248
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:107
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:221
bool IsMoving() const
Definition: eda_item.h:114
int GetRadius() const
Definition: eda_shape.cpp:961
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:1812
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:173
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:931
const VECTOR2I & GetTextPos() const
Definition: eda_text.h:260
std::deque< PAD * > & Pads()
Definition: footprint.h:211
VECTOR2I GetPosition() const override
Definition: footprint.h:229
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: footprint.cpp:1336
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:601
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: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:513
bool HasHole() const override
Definition: pad.h:106
VECTOR2I ShapePos(PCB_LAYER_ID aLayer) const
Definition: pad.cpp:1043
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
Definition: pad.h:264
const VECTOR2I & GetMid() const
Definition: pcb_track.h:337
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: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: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: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: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:652
@ 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
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_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