diff --git a/common/advanced_config.cpp b/common/advanced_config.cpp index 0564c9715d..73ce3eb808 100644 --- a/common/advanced_config.cpp +++ b/common/advanced_config.cpp @@ -119,6 +119,7 @@ static const wxChar EnableExtensionSnaps[] = wxT( "EnableExtensionSnaps" ); static const wxChar ExtensionSnapTimeoutMs[] = wxT( "ExtensionSnapTimeoutMs" ); static const wxChar ExtensionSnapActivateOnHover[] = wxT( "ExtensionSnapActivateOnHover" ); static const wxChar EnableSnapAnchorsDebug[] = wxT( "EnableSnapAnchorsDebug" ); +static const wxChar SnapHysteresis[] = wxT( "SnapHysteresis" ); static const wxChar MinParallelAngle[] = wxT( "MinParallelAngle" ); static const wxChar HoleWallPaintingMultiplier[] = wxT( "HoleWallPaintingMultiplier" ); static const wxChar MsgPanelShowUuids[] = wxT( "MsgPanelShowUuids" ); @@ -299,6 +300,7 @@ ADVANCED_CFG::ADVANCED_CFG() m_ExtensionSnapTimeoutMs = 500; m_ExtensionSnapActivateOnHover = true; m_EnableSnapAnchorsDebug = false; + m_SnapHysteresis = 5; m_MinParallelAngle = 0.001; m_HoleWallPaintingMultiplier = 1.5; @@ -592,6 +594,10 @@ void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg ) &m_EnableSnapAnchorsDebug, m_EnableSnapAnchorsDebug ) ); + m_entries.push_back( std::make_unique( true, AC_KEYS::SnapHysteresis, + &m_SnapHysteresis, m_SnapHysteresis, + 0, 100 ) ); + m_entries.push_back( std::make_unique( true, AC_KEYS::MinParallelAngle, &m_MinParallelAngle, m_MinParallelAngle, 0.0, 45.0 ) ); diff --git a/include/advanced_config.h b/include/advanced_config.h index 2beee5044f..4d2255d7ed 100644 --- a/include/advanced_config.h +++ b/include/advanced_config.h @@ -701,6 +701,15 @@ public: */ bool m_EnableSnapAnchorsDebug; + /** + * Hysteresis in pixels used for snap activation and deactivation. + * + * Setting name: "SnapHysteresis" + * Default value: 5 + * Valid values: 0 to 100 + */ + int m_SnapHysteresis; + /** * Minimum overlapping angle for which an arc is considered to be parallel * to its paired arc. diff --git a/pcbnew/tools/pcb_grid_helper.cpp b/pcbnew/tools/pcb_grid_helper.cpp index 282d038697..4214a25fd3 100644 --- a/pcbnew/tools/pcb_grid_helper.cpp +++ b/pcbnew/tools/pcb_grid_helper.cpp @@ -26,6 +26,7 @@ #include "pcb_grid_helper.h" #include +#include #include #include @@ -539,7 +540,6 @@ VECTOR2I PCB_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& a BOX2ISafe( VECTOR2D( aOrigin ) - snapRange / 2.0, VECTOR2D( snapRange, snapRange ) ); clearAnchors(); - m_snapItem = std::nullopt; const std::vector visibleItems = queryVisible( visibilityHorizon, aSkip ); computeAnchors( visibleItems, aOrigin, false, nullptr, &aLayers, false ); @@ -547,6 +547,11 @@ VECTOR2I PCB_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& a ANCHOR* nearest = nearestAnchor( aOrigin, SNAPPABLE ); VECTOR2I nearestGrid = Align( aOrigin, aGrid ); + const int hysteresisWorld = + KiROUND( m_toolMgr->GetView()->ToWorld( ADVANCED_CFG::GetCfg().m_SnapHysteresis ) ); + const int snapIn = std::max( 0, snapRange - hysteresisWorld ); + const int snapOut = snapRange + hysteresisWorld; + if( KIGFX::ANCHOR_DEBUG* ad = enableAndGetAnchorDebug(); ad ) { ad->ClearAnchors(); @@ -564,6 +569,13 @@ VECTOR2I PCB_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& a if( nearest ) snapDist = nearest->Distance( aOrigin ); + if( m_snapItem ) + { + int existingDist = m_snapItem->Distance( aOrigin ); + if( !snapDist || existingDist < *snapDist ) + snapDist = existingDist; + } + showConstructionGeometry( m_enableSnap ); SNAP_MANAGER& snapManager = getSnapManager(); @@ -635,8 +647,27 @@ VECTOR2I PCB_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& a } } + if( m_snapItem ) + { + int dist = m_snapItem->Distance( aOrigin ); + + if( dist <= snapOut ) + { + if( nearest && ptIsReferenceOnly( nearest->pos ) && + nearest->Distance( aOrigin ) <= snapRange ) + snapLineManager.SetSnapLineOrigin( nearest->pos ); + + snapLineManager.SetSnappedAnchor( m_snapItem->pos ); + updateSnapPoint( { m_snapItem->pos, m_snapItem->pointTypes } ); + + return m_snapItem->pos; + } + + m_snapItem = std::nullopt; + } + // If there's a snap anchor within range, use it if we can - if( nearest && nearest->Distance( aOrigin ) <= snapRange ) + if( nearest && nearest->Distance( aOrigin ) <= snapIn ) { const bool anchorIsConstructed = nearest->flags & ANCHOR_FLAGS::CONSTRUCTED;