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