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