|
|
|
@ -73,16 +73,11 @@ std::optional<INTERSECTABLE_GEOM> GetBoardIntersectable( const BOARD_ITEM& aItem |
|
|
|
|
|
|
|
switch( shape.GetShape() ) |
|
|
|
{ |
|
|
|
case SHAPE_T::SEGMENT: return SEG{ shape.GetStart(), shape.GetEnd() }; |
|
|
|
|
|
|
|
case SHAPE_T::CIRCLE: return CIRCLE{ shape.GetCenter(), shape.GetRadius() }; |
|
|
|
|
|
|
|
case SHAPE_T::ARC: |
|
|
|
return SHAPE_ARC{ shape.GetStart(), shape.GetArcMid(), shape.GetEnd(), 0 }; |
|
|
|
|
|
|
|
case SHAPE_T::SEGMENT: return SEG{ shape.GetStart(), shape.GetEnd() }; |
|
|
|
case SHAPE_T::CIRCLE: return CIRCLE{ shape.GetCenter(), shape.GetRadius() }; |
|
|
|
case SHAPE_T::ARC: return SHAPE_ARC{ shape.GetStart(), shape.GetArcMid(), shape.GetEnd(), 0 }; |
|
|
|
case SHAPE_T::RECTANGLE: return BOX2I::ByCorners( shape.GetStart(), shape.GetEnd() ); |
|
|
|
|
|
|
|
default: break; |
|
|
|
default: break; |
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
@ -91,14 +86,12 @@ std::optional<INTERSECTABLE_GEOM> GetBoardIntersectable( const BOARD_ITEM& aItem |
|
|
|
case PCB_TRACE_T: |
|
|
|
{ |
|
|
|
const PCB_TRACK& track = static_cast<const PCB_TRACK&>( aItem ); |
|
|
|
|
|
|
|
return SEG{ track.GetStart(), track.GetEnd() }; |
|
|
|
} |
|
|
|
|
|
|
|
case PCB_ARC_T: |
|
|
|
{ |
|
|
|
const PCB_ARC& arc = static_cast<const PCB_ARC&>( aItem ); |
|
|
|
|
|
|
|
return SHAPE_ARC{ arc.GetStart(), arc.GetMid(), arc.GetEnd(), 0 }; |
|
|
|
} |
|
|
|
|
|
|
|
@ -150,7 +143,8 @@ std::optional<int64_t> FindSquareDistanceToItem( const BOARD_ITEM& item, const V |
|
|
|
} // namespace
|
|
|
|
|
|
|
|
PCB_GRID_HELPER::PCB_GRID_HELPER( TOOL_MANAGER* aToolMgr, MAGNETIC_SETTINGS* aMagneticSettings ) : |
|
|
|
GRID_HELPER( aToolMgr, LAYER_ANCHOR ), m_magneticSettings( aMagneticSettings ) |
|
|
|
GRID_HELPER( aToolMgr, LAYER_ANCHOR ), |
|
|
|
m_magneticSettings( aMagneticSettings ) |
|
|
|
{ |
|
|
|
KIGFX::VIEW* view = m_toolMgr->GetView(); |
|
|
|
KIGFX::RENDER_SETTINGS* settings = view->GetPainter()->GetSettings(); |
|
|
|
@ -185,9 +179,7 @@ void PCB_GRID_HELPER::AddConstructionItems( std::vector<BOARD_ITEM*> aItems, boo |
|
|
|
bool aIsPersistent ) |
|
|
|
{ |
|
|
|
if( !ADVANCED_CFG::GetCfg().m_EnableExtensionSnaps ) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// For all the elements that get drawn construction geometry,
|
|
|
|
// add something suitable to the construction helper.
|
|
|
|
@ -218,10 +210,8 @@ void PCB_GRID_HELPER::AddConstructionItems( std::vector<BOARD_ITEM*> aItems, boo |
|
|
|
{ |
|
|
|
// Two rays, extending from the segment ends
|
|
|
|
const VECTOR2I segVec = shape.GetEnd() - shape.GetStart(); |
|
|
|
constructionDrawables.emplace_back( |
|
|
|
HALF_LINE{ shape.GetStart(), shape.GetStart() - segVec } ); |
|
|
|
constructionDrawables.emplace_back( |
|
|
|
HALF_LINE{ shape.GetEnd(), shape.GetEnd() + segVec } ); |
|
|
|
constructionDrawables.emplace_back( HALF_LINE{ shape.GetStart(), shape.GetStart() - segVec } ); |
|
|
|
constructionDrawables.emplace_back( HALF_LINE{ shape.GetEnd(), shape.GetEnd() + segVec } ); |
|
|
|
} |
|
|
|
|
|
|
|
if( aIsPersistent ) |
|
|
|
@ -241,17 +231,15 @@ void PCB_GRID_HELPER::AddConstructionItems( std::vector<BOARD_ITEM*> aItems, boo |
|
|
|
{ |
|
|
|
if( !aExtensionOnly ) |
|
|
|
{ |
|
|
|
constructionDrawables.push_back( |
|
|
|
CIRCLE{ shape.GetCenter(), shape.GetRadius() } ); |
|
|
|
constructionDrawables.push_back( CIRCLE{ shape.GetCenter(), shape.GetRadius() } ); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
// The rest of the circle is the arc through the opposite point to the midpoint
|
|
|
|
const VECTOR2I oppositeMid = |
|
|
|
shape.GetCenter() + ( shape.GetCenter() - shape.GetArcMid() ); |
|
|
|
constructionDrawables.push_back( |
|
|
|
SHAPE_ARC{ shape.GetStart(), oppositeMid, shape.GetEnd(), 0 } ); |
|
|
|
const VECTOR2I oppositeMid = shape.GetCenter() + ( shape.GetCenter() - shape.GetArcMid() ); |
|
|
|
constructionDrawables.push_back( SHAPE_ARC{ shape.GetStart(), oppositeMid, shape.GetEnd(), 0 } ); |
|
|
|
} |
|
|
|
|
|
|
|
constructionDrawables.push_back( shape.GetCenter() ); |
|
|
|
|
|
|
|
if( aIsPersistent ) |
|
|
|
@ -288,15 +276,11 @@ void PCB_GRID_HELPER::AddConstructionItems( std::vector<BOARD_ITEM*> aItems, boo |
|
|
|
constructionDrawables.push_back( refImg.GetPosition() ); |
|
|
|
|
|
|
|
if( refImg.GetTransformOriginOffset() != VECTOR2I( 0, 0 ) ) |
|
|
|
{ |
|
|
|
constructionDrawables.push_back( refImg.GetPosition() |
|
|
|
+ refImg.GetTransformOriginOffset() ); |
|
|
|
} |
|
|
|
constructionDrawables.push_back( refImg.GetPosition() + refImg.GetTransformOriginOffset() ); |
|
|
|
|
|
|
|
for( const SEG& seg : KIGEOM::BoxToSegs( refImg.GetBoundingBox() ) ) |
|
|
|
{ |
|
|
|
constructionDrawables.push_back( seg ); |
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
} |
|
|
|
default: |
|
|
|
@ -315,13 +299,11 @@ void PCB_GRID_HELPER::AddConstructionItems( std::vector<BOARD_ITEM*> aItems, boo |
|
|
|
} |
|
|
|
|
|
|
|
if( referenceOnlyPoints.size() ) |
|
|
|
{ |
|
|
|
getSnapManager().SetReferenceOnlyPoints( std::move( referenceOnlyPoints ) ); |
|
|
|
} |
|
|
|
|
|
|
|
// Let the manager handle it
|
|
|
|
getSnapManager().GetConstructionManager().ProposeConstructionItems( |
|
|
|
std::move( constructionItemsBatch ), aIsPersistent ); |
|
|
|
getSnapManager().GetConstructionManager().ProposeConstructionItems( std::move( constructionItemsBatch ), |
|
|
|
aIsPersistent ); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -554,6 +536,7 @@ VECTOR2I PCB_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& a |
|
|
|
if( KIGFX::ANCHOR_DEBUG* ad = enableAndGetAnchorDebug(); ad ) |
|
|
|
{ |
|
|
|
ad->ClearAnchors(); |
|
|
|
|
|
|
|
for( const ANCHOR& anchor : m_anchors ) |
|
|
|
ad->AddAnchor( anchor.pos ); |
|
|
|
|
|
|
|
@ -563,6 +546,7 @@ VECTOR2I PCB_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& a |
|
|
|
|
|
|
|
// The distance to the nearest snap point, if any
|
|
|
|
std::optional<int> snapDist; |
|
|
|
|
|
|
|
if( nearest ) |
|
|
|
snapDist = nearest->Distance( aOrigin ); |
|
|
|
|
|
|
|
@ -571,41 +555,40 @@ VECTOR2I PCB_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& a |
|
|
|
SNAP_MANAGER& snapManager = getSnapManager(); |
|
|
|
SNAP_LINE_MANAGER& snapLineManager = snapManager.GetSnapLineManager(); |
|
|
|
|
|
|
|
const auto ptIsReferenceOnly = [&]( const VECTOR2I& aPt ) |
|
|
|
{ |
|
|
|
const std::vector<VECTOR2I>& referenceOnlyPoints = snapManager.GetReferenceOnlyPoints(); |
|
|
|
return std::find( referenceOnlyPoints.begin(), referenceOnlyPoints.end(), aPt ) |
|
|
|
!= referenceOnlyPoints.end(); |
|
|
|
}; |
|
|
|
|
|
|
|
const auto proposeConstructionForItems = [&]( const std::vector<EDA_ITEM*>& aItems ) |
|
|
|
{ |
|
|
|
// Add any involved item as a temporary construction item
|
|
|
|
// (de-duplication with existing construction items is handled later)
|
|
|
|
std::vector<BOARD_ITEM*> items; |
|
|
|
|
|
|
|
for( EDA_ITEM* item : aItems ) |
|
|
|
{ |
|
|
|
BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item ); |
|
|
|
const auto ptIsReferenceOnly = |
|
|
|
[&]( const VECTOR2I& aPt ) |
|
|
|
{ |
|
|
|
const std::vector<VECTOR2I>& referenceOnlyPoints = snapManager.GetReferenceOnlyPoints(); |
|
|
|
return std::find( referenceOnlyPoints.begin(), referenceOnlyPoints.end(), aPt ) |
|
|
|
!= referenceOnlyPoints.end(); |
|
|
|
}; |
|
|
|
|
|
|
|
// Null items are allowed to arrive here as they represent geometry that isn't
|
|
|
|
// specifically tied to a board item. For example snap lines from some
|
|
|
|
// other anchor.
|
|
|
|
// But they don't produce new construction items.
|
|
|
|
if( boardItem ) |
|
|
|
const auto proposeConstructionForItems = |
|
|
|
[&]( const std::vector<EDA_ITEM*>& aItems ) |
|
|
|
{ |
|
|
|
if( m_magneticSettings->allLayers |
|
|
|
|| ( ( aLayers & boardItem->GetLayerSet() ).any() ) ) |
|
|
|
// Add any involved item as a temporary construction item
|
|
|
|
// (de-duplication with existing construction items is handled later)
|
|
|
|
std::vector<BOARD_ITEM*> items; |
|
|
|
|
|
|
|
for( EDA_ITEM* item : aItems ) |
|
|
|
{ |
|
|
|
items.push_back( boardItem ); |
|
|
|
BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item ); |
|
|
|
|
|
|
|
// Null items are allowed to arrive here as they represent geometry that isn't
|
|
|
|
// specifically tied to a board item. For example snap lines from some
|
|
|
|
// other anchor.
|
|
|
|
// But they don't produce new construction items.
|
|
|
|
if( boardItem ) |
|
|
|
{ |
|
|
|
if( m_magneticSettings->allLayers || ( ( aLayers & boardItem->GetLayerSet() ).any() ) ) |
|
|
|
items.push_back( boardItem ); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Temporary construction items are not persistent and don't
|
|
|
|
// overlay the items themselves (as the items will not be moved)
|
|
|
|
AddConstructionItems( items, true, false ); |
|
|
|
}; |
|
|
|
// Temporary construction items are not persistent and don't
|
|
|
|
// overlay the items themselves (as the items will not be moved)
|
|
|
|
AddConstructionItems( items, true, false ); |
|
|
|
}; |
|
|
|
|
|
|
|
bool snapValid = false; |
|
|
|
|
|
|
|
@ -631,9 +614,7 @@ VECTOR2I PCB_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& a |
|
|
|
// (we don't snap to reference points, but we can use them to update the snap line,
|
|
|
|
// without actually snapping)
|
|
|
|
if( !ptIsReferenceOnly( *snapLineSnap ) ) |
|
|
|
{ |
|
|
|
return *snapLineSnap; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -658,15 +639,13 @@ VECTOR2I PCB_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& a |
|
|
|
// This is to make construction items less intrusive and more
|
|
|
|
// a result of user intent.
|
|
|
|
if( !anchorIsConstructed ) |
|
|
|
{ |
|
|
|
proposeConstructionForItems( nearest->items ); |
|
|
|
} |
|
|
|
|
|
|
|
const auto shouldAcceptAnchor = [&]( const ANCHOR& aAnchor ) |
|
|
|
{ |
|
|
|
// If no extension snaps are enabled, don't inhibit
|
|
|
|
static const bool haveExtensions = |
|
|
|
ADVANCED_CFG::GetCfg().m_EnableExtensionSnaps; |
|
|
|
static const bool haveExtensions = ADVANCED_CFG::GetCfg().m_EnableExtensionSnaps; |
|
|
|
|
|
|
|
if( !haveExtensions ) |
|
|
|
return true; |
|
|
|
|
|
|
|
@ -677,8 +656,7 @@ VECTOR2I PCB_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& a |
|
|
|
// This is an area most likely to be controversial/need tuning,
|
|
|
|
// as some users will think it's fiddly; without 'activation', others will
|
|
|
|
// think the snaps are intrusive.
|
|
|
|
bool allRealAreInvolved = |
|
|
|
snapManager.GetConstructionManager().InvolvesAllGivenRealItems( |
|
|
|
bool allRealAreInvolved = snapManager.GetConstructionManager().InvolvesAllGivenRealItems( |
|
|
|
aAnchor.items ); |
|
|
|
return allRealAreInvolved; |
|
|
|
}; |
|
|
|
@ -700,14 +678,15 @@ VECTOR2I PCB_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& a |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
static const bool canActivateByHitTest = |
|
|
|
ADVANCED_CFG::GetCfg().m_ExtensionSnapActivateOnHover; |
|
|
|
static const bool canActivateByHitTest = ADVANCED_CFG::GetCfg().m_ExtensionSnapActivateOnHover; |
|
|
|
|
|
|
|
if( canActivateByHitTest ) |
|
|
|
{ |
|
|
|
// An exact hit on an item, even if not near a snap point
|
|
|
|
// If it's tool hard to hit by hover, this can be increased
|
|
|
|
// to make it non-exact.
|
|
|
|
const int hoverAccuracy = 0; |
|
|
|
|
|
|
|
for( BOARD_ITEM* item : visibleItems ) |
|
|
|
{ |
|
|
|
if( item->HitTest( aOrigin, hoverAccuracy ) ) |
|
|
|
@ -726,12 +705,10 @@ VECTOR2I PCB_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& a |
|
|
|
// but they're useful when there isn't a grid to snap to
|
|
|
|
if( !m_enableGrid ) |
|
|
|
{ |
|
|
|
OPT_VECTOR2I nearestPointOnAnElement = |
|
|
|
GetNearestPoint( m_pointOnLineCandidates, aOrigin ); |
|
|
|
OPT_VECTOR2I nearestPointOnAnElement = GetNearestPoint( m_pointOnLineCandidates, aOrigin ); |
|
|
|
|
|
|
|
// Got any nearest point - snap if in range
|
|
|
|
if( nearestPointOnAnElement |
|
|
|
&& nearestPointOnAnElement->Distance( aOrigin ) <= snapRange ) |
|
|
|
if( nearestPointOnAnElement && nearestPointOnAnElement->Distance( aOrigin ) <= snapRange ) |
|
|
|
{ |
|
|
|
updateSnapPoint( { *nearestPointOnAnElement, POINT_TYPE::PT_ON_ELEMENT } ); |
|
|
|
|
|
|
|
@ -954,49 +931,46 @@ void PCB_GRID_HELPER::computeAnchors( const std::vector<BOARD_ITEM*>& aItems, |
|
|
|
const bool excludeGraphics = aSelectionFilter && !aSelectionFilter->graphics; |
|
|
|
const bool excludeTracks = aSelectionFilter && !aSelectionFilter->tracks; |
|
|
|
|
|
|
|
const auto itemIsSnappable = [&]( const BOARD_ITEM& aItem ) |
|
|
|
{ |
|
|
|
// If we are filtering by layers, check if the item matches
|
|
|
|
if( aMatchLayers ) |
|
|
|
{ |
|
|
|
return m_magneticSettings->allLayers |
|
|
|
|| ( ( *aMatchLayers & aItem.GetLayerSet() ).any() ); |
|
|
|
} |
|
|
|
return true; |
|
|
|
}; |
|
|
|
|
|
|
|
const auto processItem = [&]( BOARD_ITEM& item ) |
|
|
|
{ |
|
|
|
// Don't even process the item if it doesn't match the layers
|
|
|
|
if( !itemIsSnappable( item ) ) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
const auto itemIsSnappable = |
|
|
|
[&]( const BOARD_ITEM& aItem ) |
|
|
|
{ |
|
|
|
// If we are filtering by layers, check if the item matches
|
|
|
|
if( aMatchLayers ) |
|
|
|
return m_magneticSettings->allLayers || ( ( *aMatchLayers & aItem.GetLayerSet() ).any() ); |
|
|
|
|
|
|
|
// First, add all the key points of the item itself
|
|
|
|
computeAnchors( &item, aRefPos, aFrom, aSelectionFilter ); |
|
|
|
return true; |
|
|
|
}; |
|
|
|
|
|
|
|
// If we are computing intersections, construct the relevant intersectables
|
|
|
|
// Points on elements also use the intersectables.
|
|
|
|
if( computeIntersections || computePointsOnElements ) |
|
|
|
{ |
|
|
|
std::optional<INTERSECTABLE_GEOM> intersectableGeom; |
|
|
|
if( !excludeGraphics |
|
|
|
&& ( item.Type() == PCB_SHAPE_T || item.Type() == PCB_REFERENCE_IMAGE_T ) ) |
|
|
|
{ |
|
|
|
intersectableGeom = GetBoardIntersectable( item ); |
|
|
|
} |
|
|
|
else if( !excludeTracks && ( item.Type() == PCB_TRACE_T || item.Type() == PCB_ARC_T ) ) |
|
|
|
const auto processItem = |
|
|
|
[&]( BOARD_ITEM& item ) |
|
|
|
{ |
|
|
|
intersectableGeom = GetBoardIntersectable( item ); |
|
|
|
} |
|
|
|
// Don't even process the item if it doesn't match the layers
|
|
|
|
if( !itemIsSnappable( item ) ) |
|
|
|
return; |
|
|
|
|
|
|
|
if( intersectableGeom ) |
|
|
|
{ |
|
|
|
intersectables.emplace_back( &item, *intersectableGeom ); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
// First, add all the key points of the item itself
|
|
|
|
computeAnchors( &item, aRefPos, aFrom, aSelectionFilter ); |
|
|
|
|
|
|
|
// If we are computing intersections, construct the relevant intersectables
|
|
|
|
// Points on elements also use the intersectables.
|
|
|
|
if( computeIntersections || computePointsOnElements ) |
|
|
|
{ |
|
|
|
std::optional<INTERSECTABLE_GEOM> intersectableGeom; |
|
|
|
|
|
|
|
if( !excludeGraphics |
|
|
|
&& ( item.Type() == PCB_SHAPE_T || item.Type() == PCB_REFERENCE_IMAGE_T ) ) |
|
|
|
{ |
|
|
|
intersectableGeom = GetBoardIntersectable( item ); |
|
|
|
} |
|
|
|
else if( !excludeTracks && ( item.Type() == PCB_TRACE_T || item.Type() == PCB_ARC_T ) ) |
|
|
|
{ |
|
|
|
intersectableGeom = GetBoardIntersectable( item ); |
|
|
|
} |
|
|
|
|
|
|
|
if( intersectableGeom ) |
|
|
|
intersectables.emplace_back( &item, *intersectableGeom ); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
for( BOARD_ITEM* item : aItems ) |
|
|
|
{ |
|
|
|
@ -1080,6 +1054,7 @@ void PCB_GRID_HELPER::computeAnchors( const std::vector<BOARD_ITEM*>& aItems, |
|
|
|
// The intersectables can also be used for fall-back snapping to "point on line"
|
|
|
|
// snaps if no other snap is found
|
|
|
|
m_pointOnLineCandidates.clear(); |
|
|
|
|
|
|
|
if( computePointsOnElements ) |
|
|
|
{ |
|
|
|
// For the moment, it's trivial to make a NEARABLE from an INTERSECTABLE,
|
|
|
|
@ -1176,144 +1151,143 @@ void PCB_GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos |
|
|
|
}; |
|
|
|
|
|
|
|
// As defaults, these are probably reasonable to avoid spamming key points
|
|
|
|
const KIGEOM::OVAL_KEY_POINT_FLAGS ovalKeyPointFlags = |
|
|
|
KIGEOM::OVAL_CENTER | KIGEOM::OVAL_CAP_TIPS | KIGEOM::OVAL_SIDE_MIDPOINTS |
|
|
|
| KIGEOM::OVAL_CARDINAL_EXTREMES; |
|
|
|
|
|
|
|
auto handlePadShape = [&]( PAD* aPad, PCB_LAYER_ID aLayer ) |
|
|
|
{ |
|
|
|
addAnchor( aPad->GetPosition(), ORIGIN | SNAPPABLE, aPad, POINT_TYPE::PT_CENTER ); |
|
|
|
|
|
|
|
/// If we are getting a drag point, we don't want to center the edge of pads
|
|
|
|
if( aFrom ) |
|
|
|
return; |
|
|
|
|
|
|
|
switch( aPad->GetShape( aLayer ) ) |
|
|
|
{ |
|
|
|
case PAD_SHAPE::CIRCLE: |
|
|
|
{ |
|
|
|
const CIRCLE circle( aPad->ShapePos( aLayer ), aPad->GetSizeX() / 2 ); |
|
|
|
const KIGEOM::OVAL_KEY_POINT_FLAGS ovalKeyPointFlags = KIGEOM::OVAL_CENTER |
|
|
|
| KIGEOM::OVAL_CAP_TIPS |
|
|
|
| KIGEOM::OVAL_SIDE_MIDPOINTS |
|
|
|
| KIGEOM::OVAL_CARDINAL_EXTREMES; |
|
|
|
|
|
|
|
for( const TYPED_POINT2I& pt : KIGEOM::GetCircleKeyPoints( circle, false ) ) |
|
|
|
auto handlePadShape = |
|
|
|
[&]( PAD* aPad, PCB_LAYER_ID aLayer ) |
|
|
|
{ |
|
|
|
addAnchor( pt.m_point, OUTLINE | SNAPPABLE, aPad, pt.m_types ); |
|
|
|
} |
|
|
|
addAnchor( aPad->GetPosition(), ORIGIN | SNAPPABLE, aPad, POINT_TYPE::PT_CENTER ); |
|
|
|
|
|
|
|
break; |
|
|
|
} |
|
|
|
case PAD_SHAPE::OVAL: |
|
|
|
{ |
|
|
|
const OVAL oval( aPad->GetSize( aLayer ), aPad->GetPosition(), aPad->GetOrientation() ); |
|
|
|
|
|
|
|
for( const TYPED_POINT2I& pt : KIGEOM::GetOvalKeyPoints( oval, ovalKeyPointFlags ) ) |
|
|
|
{ |
|
|
|
addAnchor( pt.m_point, OUTLINE | SNAPPABLE, aPad, pt.m_types ); |
|
|
|
} |
|
|
|
/// If we are getting a drag point, we don't want to center the edge of pads
|
|
|
|
if( aFrom ) |
|
|
|
return; |
|
|
|
|
|
|
|
break; |
|
|
|
} |
|
|
|
case PAD_SHAPE::RECTANGLE: |
|
|
|
case PAD_SHAPE::TRAPEZOID: |
|
|
|
case PAD_SHAPE::ROUNDRECT: |
|
|
|
case PAD_SHAPE::CHAMFERED_RECT: |
|
|
|
{ |
|
|
|
VECTOR2I half_size( aPad->GetSize( aLayer ) / 2 ); |
|
|
|
VECTOR2I trap_delta( 0, 0 ); |
|
|
|
switch( aPad->GetShape( aLayer ) ) |
|
|
|
{ |
|
|
|
case PAD_SHAPE::CIRCLE: |
|
|
|
{ |
|
|
|
const CIRCLE circle( aPad->ShapePos( aLayer ), aPad->GetSizeX() / 2 ); |
|
|
|
|
|
|
|
if( aPad->GetShape( aLayer ) == PAD_SHAPE::TRAPEZOID ) |
|
|
|
trap_delta = aPad->GetDelta( aLayer ) / 2; |
|
|
|
for( const TYPED_POINT2I& pt : KIGEOM::GetCircleKeyPoints( circle, false ) ) |
|
|
|
addAnchor( pt.m_point, OUTLINE | SNAPPABLE, aPad, pt.m_types ); |
|
|
|
|
|
|
|
SHAPE_LINE_CHAIN corners; |
|
|
|
break; |
|
|
|
} |
|
|
|
case PAD_SHAPE::OVAL: |
|
|
|
{ |
|
|
|
const OVAL oval( aPad->GetSize( aLayer ), aPad->GetPosition(), aPad->GetOrientation() ); |
|
|
|
|
|
|
|
corners.Append( -half_size.x - trap_delta.y, half_size.y + trap_delta.x ); |
|
|
|
corners.Append( half_size.x + trap_delta.y, half_size.y - trap_delta.x ); |
|
|
|
corners.Append( half_size.x - trap_delta.y, -half_size.y + trap_delta.x ); |
|
|
|
corners.Append( -half_size.x + trap_delta.y, -half_size.y - trap_delta.x ); |
|
|
|
corners.SetClosed( true ); |
|
|
|
for( const TYPED_POINT2I& pt : KIGEOM::GetOvalKeyPoints( oval, ovalKeyPointFlags ) ) |
|
|
|
addAnchor( pt.m_point, OUTLINE | SNAPPABLE, aPad, pt.m_types ); |
|
|
|
|
|
|
|
corners.Rotate( aPad->GetOrientation() ); |
|
|
|
corners.Move( aPad->ShapePos( aLayer ) ); |
|
|
|
break; |
|
|
|
} |
|
|
|
case PAD_SHAPE::RECTANGLE: |
|
|
|
case PAD_SHAPE::TRAPEZOID: |
|
|
|
case PAD_SHAPE::ROUNDRECT: |
|
|
|
case PAD_SHAPE::CHAMFERED_RECT: |
|
|
|
{ |
|
|
|
VECTOR2I half_size( aPad->GetSize( aLayer ) / 2 ); |
|
|
|
VECTOR2I trap_delta( 0, 0 ); |
|
|
|
|
|
|
|
for( std::size_t ii = 0; ii < corners.GetSegmentCount(); ++ii ) |
|
|
|
{ |
|
|
|
const SEG& seg = corners.GetSegment( ii ); |
|
|
|
addAnchor( seg.A, OUTLINE | SNAPPABLE, aPad, POINT_TYPE::PT_CORNER ); |
|
|
|
addAnchor( seg.Center(), OUTLINE | SNAPPABLE, aPad, POINT_TYPE::PT_MID ); |
|
|
|
if( aPad->GetShape( aLayer ) == PAD_SHAPE::TRAPEZOID ) |
|
|
|
trap_delta = aPad->GetDelta( aLayer ) / 2; |
|
|
|
|
|
|
|
if( ii == corners.GetSegmentCount() - 1 ) |
|
|
|
addAnchor( seg.B, OUTLINE | SNAPPABLE, aPad, POINT_TYPE::PT_CORNER ); |
|
|
|
} |
|
|
|
SHAPE_LINE_CHAIN corners; |
|
|
|
|
|
|
|
break; |
|
|
|
} |
|
|
|
corners.Append( -half_size.x - trap_delta.y, half_size.y + trap_delta.x ); |
|
|
|
corners.Append( half_size.x + trap_delta.y, half_size.y - trap_delta.x ); |
|
|
|
corners.Append( half_size.x - trap_delta.y, -half_size.y + trap_delta.x ); |
|
|
|
corners.Append( -half_size.x + trap_delta.y, -half_size.y - trap_delta.x ); |
|
|
|
corners.SetClosed( true ); |
|
|
|
|
|
|
|
default: |
|
|
|
{ |
|
|
|
const auto& outline = aPad->GetEffectivePolygon( aLayer, ERROR_INSIDE ); |
|
|
|
corners.Rotate( aPad->GetOrientation() ); |
|
|
|
corners.Move( aPad->ShapePos( aLayer ) ); |
|
|
|
|
|
|
|
if( !outline->IsEmpty() ) |
|
|
|
{ |
|
|
|
for( const VECTOR2I& pt : outline->Outline( 0 ).CPoints() ) |
|
|
|
addAnchor( pt, OUTLINE | SNAPPABLE, aPad ); |
|
|
|
} |
|
|
|
for( std::size_t ii = 0; ii < corners.GetSegmentCount(); ++ii ) |
|
|
|
{ |
|
|
|
const SEG& seg = corners.GetSegment( ii ); |
|
|
|
addAnchor( seg.A, OUTLINE | SNAPPABLE, aPad, POINT_TYPE::PT_CORNER ); |
|
|
|
addAnchor( seg.Center(), OUTLINE | SNAPPABLE, aPad, POINT_TYPE::PT_MID ); |
|
|
|
|
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
if( ii == corners.GetSegmentCount() - 1 ) |
|
|
|
addAnchor( seg.B, OUTLINE | SNAPPABLE, aPad, POINT_TYPE::PT_CORNER ); |
|
|
|
} |
|
|
|
|
|
|
|
if( aPad->HasHole() ) |
|
|
|
{ |
|
|
|
// Holes are at the pad centre (it's the shape that may be offset)
|
|
|
|
const VECTOR2I hole_pos = aPad->GetPosition(); |
|
|
|
const VECTOR2I hole_size = aPad->GetDrillSize(); |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
std::vector<TYPED_POINT2I> snap_pts; |
|
|
|
default: |
|
|
|
{ |
|
|
|
const auto& outline = aPad->GetEffectivePolygon( aLayer, ERROR_INSIDE ); |
|
|
|
|
|
|
|
if( hole_size.x == hole_size.y ) |
|
|
|
{ |
|
|
|
// Circle
|
|
|
|
const CIRCLE circle( hole_pos, hole_size.x / 2 ); |
|
|
|
snap_pts = KIGEOM::GetCircleKeyPoints( circle, true ); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
// Oval
|
|
|
|
if( !outline->IsEmpty() ) |
|
|
|
{ |
|
|
|
for( const VECTOR2I& pt : outline->Outline( 0 ).CPoints() ) |
|
|
|
addAnchor( pt, OUTLINE | SNAPPABLE, aPad ); |
|
|
|
} |
|
|
|
|
|
|
|
// For now there's no way to have an off-angle hole, so this is the
|
|
|
|
// same as the pad. In future, this may not be true:
|
|
|
|
// https://gitlab.com/kicad/code/kicad/-/issues/4124
|
|
|
|
const OVAL oval( hole_size, hole_pos, aPad->GetOrientation() ); |
|
|
|
snap_pts = KIGEOM::GetOvalKeyPoints( oval, ovalKeyPointFlags ); |
|
|
|
} |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
for( const TYPED_POINT2I& snap_pt : snap_pts ) |
|
|
|
addAnchor( snap_pt.m_point, OUTLINE | SNAPPABLE, aPad, snap_pt.m_types ); |
|
|
|
} |
|
|
|
}; |
|
|
|
if( aPad->HasHole() ) |
|
|
|
{ |
|
|
|
// Holes are at the pad centre (it's the shape that may be offset)
|
|
|
|
const VECTOR2I hole_pos = aPad->GetPosition(); |
|
|
|
const VECTOR2I hole_size = aPad->GetDrillSize(); |
|
|
|
|
|
|
|
const auto addRectPoints = [&]( const BOX2I& aBox, EDA_ITEM& aRelatedItem ) |
|
|
|
{ |
|
|
|
const VECTOR2I topRight( aBox.GetRight(), aBox.GetTop() ); |
|
|
|
const VECTOR2I bottomLeft( aBox.GetLeft(), aBox.GetBottom() ); |
|
|
|
std::vector<TYPED_POINT2I> snap_pts; |
|
|
|
|
|
|
|
const SEG first( aBox.GetOrigin(), topRight ); |
|
|
|
const SEG second( topRight, aBox.GetEnd() ); |
|
|
|
const SEG third( aBox.GetEnd(), bottomLeft ); |
|
|
|
const SEG fourth( bottomLeft, aBox.GetOrigin() ); |
|
|
|
if( hole_size.x == hole_size.y ) |
|
|
|
{ |
|
|
|
// Circle
|
|
|
|
const CIRCLE circle( hole_pos, hole_size.x / 2 ); |
|
|
|
snap_pts = KIGEOM::GetCircleKeyPoints( circle, true ); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
// Oval
|
|
|
|
|
|
|
|
const int snapFlags = CORNER | SNAPPABLE; |
|
|
|
// For now there's no way to have an off-angle hole, so this is the
|
|
|
|
// same as the pad. In future, this may not be true:
|
|
|
|
// https://gitlab.com/kicad/code/kicad/-/issues/4124
|
|
|
|
const OVAL oval( hole_size, hole_pos, aPad->GetOrientation() ); |
|
|
|
snap_pts = KIGEOM::GetOvalKeyPoints( oval, ovalKeyPointFlags ); |
|
|
|
} |
|
|
|
|
|
|
|
addAnchor( aBox.GetCenter(), snapFlags, &aRelatedItem, POINT_TYPE::PT_CENTER ); |
|
|
|
for( const TYPED_POINT2I& snap_pt : snap_pts ) |
|
|
|
addAnchor( snap_pt.m_point, OUTLINE | SNAPPABLE, aPad, snap_pt.m_types ); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
addAnchor( first.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER ); |
|
|
|
addAnchor( first.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID ); |
|
|
|
addAnchor( second.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER ); |
|
|
|
addAnchor( second.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID ); |
|
|
|
addAnchor( third.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER ); |
|
|
|
addAnchor( third.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID ); |
|
|
|
addAnchor( fourth.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER ); |
|
|
|
addAnchor( fourth.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID ); |
|
|
|
}; |
|
|
|
const auto addRectPoints = |
|
|
|
[&]( const BOX2I& aBox, EDA_ITEM& aRelatedItem ) |
|
|
|
{ |
|
|
|
const VECTOR2I topRight( aBox.GetRight(), aBox.GetTop() ); |
|
|
|
const VECTOR2I bottomLeft( aBox.GetLeft(), aBox.GetBottom() ); |
|
|
|
|
|
|
|
const SEG first( aBox.GetOrigin(), topRight ); |
|
|
|
const SEG second( topRight, aBox.GetEnd() ); |
|
|
|
const SEG third( aBox.GetEnd(), bottomLeft ); |
|
|
|
const SEG fourth( bottomLeft, aBox.GetOrigin() ); |
|
|
|
|
|
|
|
const int snapFlags = CORNER | SNAPPABLE; |
|
|
|
|
|
|
|
addAnchor( aBox.GetCenter(), snapFlags, &aRelatedItem, POINT_TYPE::PT_CENTER ); |
|
|
|
|
|
|
|
addAnchor( first.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER ); |
|
|
|
addAnchor( first.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID ); |
|
|
|
addAnchor( second.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER ); |
|
|
|
addAnchor( second.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID ); |
|
|
|
addAnchor( third.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER ); |
|
|
|
addAnchor( third.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID ); |
|
|
|
addAnchor( fourth.A, snapFlags, &aRelatedItem, POINT_TYPE::PT_CORNER ); |
|
|
|
addAnchor( fourth.Center(), snapFlags, &aRelatedItem, POINT_TYPE::PT_MID ); |
|
|
|
}; |
|
|
|
|
|
|
|
const auto handleShape = |
|
|
|
[&]( PCB_SHAPE* shape ) |
|
|
|
@ -1329,26 +1303,18 @@ void PCB_GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos |
|
|
|
|
|
|
|
addAnchor( start, ORIGIN | SNAPPABLE, shape, POINT_TYPE::PT_CENTER ); |
|
|
|
|
|
|
|
addAnchor( start + VECTOR2I( -r, 0 ), OUTLINE | SNAPPABLE, shape, |
|
|
|
POINT_TYPE::PT_QUADRANT ); |
|
|
|
addAnchor( start + VECTOR2I( r, 0 ), OUTLINE | SNAPPABLE, shape, |
|
|
|
POINT_TYPE::PT_QUADRANT ); |
|
|
|
addAnchor( start + VECTOR2I( 0, -r ), OUTLINE | SNAPPABLE, shape, |
|
|
|
POINT_TYPE::PT_QUADRANT ); |
|
|
|
addAnchor( start + VECTOR2I( 0, r ), OUTLINE | SNAPPABLE, shape, |
|
|
|
POINT_TYPE::PT_QUADRANT ); |
|
|
|
addAnchor( start + VECTOR2I( -r, 0 ), OUTLINE | SNAPPABLE, shape, POINT_TYPE::PT_QUADRANT ); |
|
|
|
addAnchor( start + VECTOR2I( r, 0 ), OUTLINE | SNAPPABLE, shape, POINT_TYPE::PT_QUADRANT ); |
|
|
|
addAnchor( start + VECTOR2I( 0, -r ), OUTLINE | SNAPPABLE, shape, POINT_TYPE::PT_QUADRANT ); |
|
|
|
addAnchor( start + VECTOR2I( 0, r ), OUTLINE | SNAPPABLE, shape, POINT_TYPE::PT_QUADRANT ); |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
case SHAPE_T::ARC: |
|
|
|
addAnchor( shape->GetStart(), CORNER | SNAPPABLE, shape, |
|
|
|
POINT_TYPE::PT_END ); |
|
|
|
addAnchor( shape->GetEnd(), CORNER | SNAPPABLE, shape, |
|
|
|
POINT_TYPE::PT_END ); |
|
|
|
addAnchor( shape->GetArcMid(), CORNER | SNAPPABLE, shape, |
|
|
|
POINT_TYPE::PT_MID ); |
|
|
|
addAnchor( shape->GetCenter(), ORIGIN | SNAPPABLE, shape, |
|
|
|
POINT_TYPE::PT_CENTER ); |
|
|
|
addAnchor( shape->GetStart(), CORNER | SNAPPABLE, shape, POINT_TYPE::PT_END ); |
|
|
|
addAnchor( shape->GetEnd(), CORNER | SNAPPABLE, shape, POINT_TYPE::PT_END ); |
|
|
|
addAnchor( shape->GetArcMid(), CORNER | SNAPPABLE, shape, POINT_TYPE::PT_MID ); |
|
|
|
addAnchor( shape->GetCenter(), ORIGIN | SNAPPABLE, shape, POINT_TYPE::PT_CENTER ); |
|
|
|
break; |
|
|
|
|
|
|
|
case SHAPE_T::RECTANGLE: |
|
|
|
@ -1360,8 +1326,7 @@ void PCB_GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos |
|
|
|
case SHAPE_T::SEGMENT: |
|
|
|
addAnchor( start, CORNER | SNAPPABLE, shape, POINT_TYPE::PT_END ); |
|
|
|
addAnchor( end, CORNER | SNAPPABLE, shape, POINT_TYPE::PT_END ); |
|
|
|
addAnchor( shape->GetCenter(), CORNER | SNAPPABLE, shape, |
|
|
|
POINT_TYPE::PT_MID ); |
|
|
|
addAnchor( shape->GetCenter(), CORNER | SNAPPABLE, shape, POINT_TYPE::PT_MID ); |
|
|
|
break; |
|
|
|
|
|
|
|
case SHAPE_T::POLY: |
|
|
|
@ -1423,7 +1388,9 @@ void PCB_GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos |
|
|
|
if( !isHighContrast |
|
|
|
|| PadstackUniqueLayerAppliesToLayer( pad->Padstack(), aLayer, |
|
|
|
activeHighContrastPrimaryLayer ) ) |
|
|
|
{ |
|
|
|
handlePadShape( pad, aLayer ); |
|
|
|
} |
|
|
|
} ); |
|
|
|
} |
|
|
|
|
|
|
|
@ -1467,7 +1434,9 @@ void PCB_GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos |
|
|
|
if( !isHighContrast |
|
|
|
|| PadstackUniqueLayerAppliesToLayer( pad->Padstack(), aLayer, |
|
|
|
activeHighContrastPrimaryLayer ) ) |
|
|
|
{ |
|
|
|
handlePadShape( pad, aLayer ); |
|
|
|
} |
|
|
|
} ); |
|
|
|
} |
|
|
|
|
|
|
|
@ -1567,8 +1536,7 @@ void PCB_GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos |
|
|
|
|
|
|
|
case PCB_MARKER_T: |
|
|
|
case PCB_TARGET_T: |
|
|
|
addAnchor( aItem->GetPosition(), ORIGIN | CORNER | SNAPPABLE, aItem, |
|
|
|
POINT_TYPE::PT_CENTER ); |
|
|
|
addAnchor( aItem->GetPosition(), ORIGIN | CORNER | SNAPPABLE, aItem, POINT_TYPE::PT_CENTER ); |
|
|
|
break; |
|
|
|
|
|
|
|
case PCB_VIA_T: |
|
|
|
@ -1584,8 +1552,7 @@ void PCB_GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos |
|
|
|
} |
|
|
|
|
|
|
|
if( checkVisibility( aItem ) ) |
|
|
|
addAnchor( aItem->GetPosition(), ORIGIN | CORNER | SNAPPABLE, aItem, |
|
|
|
POINT_TYPE::PT_CENTER ); |
|
|
|
addAnchor( aItem->GetPosition(), ORIGIN | CORNER | SNAPPABLE, aItem, POINT_TYPE::PT_CENTER ); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
@ -1703,10 +1670,9 @@ void PCB_GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos |
|
|
|
|
|
|
|
if( checkVisibility( aItem ) ) |
|
|
|
{ |
|
|
|
const PCB_REFERENCE_IMAGE& image = |
|
|
|
static_cast<const PCB_REFERENCE_IMAGE&>( *aItem ); |
|
|
|
const REFERENCE_IMAGE& refImg = image.GetReferenceImage(); |
|
|
|
const BOX2I bbox = refImg.GetBoundingBox(); |
|
|
|
const PCB_REFERENCE_IMAGE& image = static_cast<const PCB_REFERENCE_IMAGE&>( *aItem ); |
|
|
|
const REFERENCE_IMAGE& refImg = image.GetReferenceImage(); |
|
|
|
const BOX2I bbox = refImg.GetBoundingBox(); |
|
|
|
|
|
|
|
addRectPoints( bbox, *aItem ); |
|
|
|
|
|
|
|
@ -1748,6 +1714,7 @@ PCB_GRID_HELPER::ANCHOR* PCB_GRID_HELPER::nearestAnchor( const VECTOR2I& aPos, i |
|
|
|
else |
|
|
|
{ |
|
|
|
const double dist = anchor.pos.SquaredDistance( aPos ); |
|
|
|
|
|
|
|
if( dist < minDist ) |
|
|
|
{ |
|
|
|
// New minimum distance
|
|
|
|
|