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