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