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