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