SnapDistance
SnapDistance
#Overview
name: SnapDistance
The value of this variable can be defined or overridden in .ini config files. 1
.ini config file referencing this setting variable.
It is referenced in 19
C++ source files.
#Summary
#Usage in the C++ source code
The purpose of SnapDistance is to define a threshold distance for various snapping operations in Unreal Engine 5. It is primarily used in different subsystems and plugins to determine when objects, vertices, or other elements should be considered close enough to snap together or align.
This setting variable is utilized in several Unreal Engine subsystems, plugins, and modules, including:
- Avalanche Viewport plugin
- Floating Properties plugin
- Planar Cut plugin
- Trace Insights system
- UMG Editor
- Level Editor
- Unreal Editor
- Chaos physics system
The value of this variable is typically set in configuration files or through editor settings. For example, in the LevelEditorViewportSettings class, it is defined as a config property:
UPROPERTY(config)
float SnapDistance;
SnapDistance often interacts with other variables and functions related to spatial operations, such as vertex hashing, collision detection, and UI element positioning.
Developers should be aware that:
- The appropriate value for SnapDistance can vary depending on the scale and precision requirements of the specific use case.
- Using too large a SnapDistance can result in unintended snapping or merging of elements that should remain separate.
- Using too small a SnapDistance might fail to snap elements that should be aligned.
Best practices when using this variable include:
- Adjusting the SnapDistance based on the scale of your project and the specific needs of each system using it.
- Consider providing user-configurable options for SnapDistance in editor tools to allow for fine-tuning.
- Be consistent in how SnapDistance is applied across related systems to ensure predictable behavior.
- When using SnapDistance for performance-critical operations, consider the trade-off between precision and computational cost.
#Setting Variables
#References In INI files
Location: <Workspace>/Engine/Config/BaseEditorPerProjectUserSettings.ini:427, section: [/Script/UnrealEd.LevelEditorViewportSettings]
- INI Section:
/Script/UnrealEd.LevelEditorViewportSettings
- Raw value:
10.000000
- Is Array:
False
#References in C++ code
#Callsites
This variable is referenced in the following C++ source code:
#Loc: <Workspace>/Engine/Plugins/Experimental/Avalanche/Source/AvalancheViewport/Private/Interaction/AvaSnapOperation.cpp:925
Scope (from outer to inner):
file
function bool FAvaSnapOperation::SnapLocation
Source code excerpt:
int32 ClosestSnapPointIdx;
bool bSnappedToPoint;
float SnapDistance;
};
float SnapDistanceX;
float SnapDistanceY;
int32 AnchorClosestSnappedToPointIdxXLocal;
int32 AnchorClosestSnappedToPointIdxYLocal;
#Loc: <Workspace>/Engine/Plugins/Experimental/Avalanche/Source/AvalancheViewport/Private/Interaction/AvaSnapOperation.cpp:1152
Scope (from outer to inner):
file
function bool FAvaSnapOperation::SnapDragLocation
Source code excerpt:
int32 ClosestSnapPointIdx;
bool bSnappedToPoint;
double SnapDistance;
};
int32 ClosestActorScreenSnapPointIdxX = INDEX_NONE;
int32 ClosestActorScreenSnapPointIdxY = INDEX_NONE;
float ClosestActorScreenSnapPointDistanceX = -1;
float ClosestActorScreenSnapPointDistanceY = -1;
float SnapDistance;
TArray<bool> bWasAnchorSnappedX;
TArray<bool> bWasAnchorSnappedY;
TArray<int32> AnchorClosestSnappedToPointIdxX;
TArray<int32> AnchorClosestSnappedToPointIdxY;
bWasAnchorSnappedX.SetNum(InDraggedActorSnapPoints.Num());
#Loc: <Workspace>/Engine/Plugins/Experimental/Avalanche/Source/AvalancheViewport/Private/Interaction/AvaSnapOperation.cpp:1176
Scope (from outer to inner):
file
function bool FAvaSnapOperation::SnapDragLocation
Source code excerpt:
{
bWasAnchorSnappedX[ActorScreenSnapPointIdx] = SnapX(DraggedActorsScreenSpaceSnapLocations_Snapped[ActorScreenSnapPointIdx].X,
ViewportSize.X, AnchorClosestSnappedToPointIdxX[ActorScreenSnapPointIdx], SnapDistance);
if (SnapDistance >= 0 && (ClosestActorScreenSnapPointDistanceX < 0 || SnapDistance < ClosestActorScreenSnapPointDistanceX))
{
ClosestActorScreenSnapPointIdxX = ActorScreenSnapPointIdx;
ClosestActorScreenSnapPointDistanceX = SnapDistance;
}
}
if (bSnapY)
{
bWasAnchorSnappedY[ActorScreenSnapPointIdx] = SnapY(DraggedActorsScreenSpaceSnapLocations_Snapped[ActorScreenSnapPointIdx].Y,
ViewportSize.Y, AnchorClosestSnappedToPointIdxY[ActorScreenSnapPointIdx], SnapDistance);
if (SnapDistance >= 0 && (ClosestActorScreenSnapPointDistanceY < 0 || SnapDistance < ClosestActorScreenSnapPointDistanceY))
{
ClosestActorScreenSnapPointIdxY = ActorScreenSnapPointIdx;
ClosestActorScreenSnapPointDistanceY = SnapDistance;
}
}
}
bSnappedToLinkX = (ClosestActorScreenSnapPointIdxX != INDEX_NONE && ClosestActorScreenSnapPointDistanceX >= 0 && ClosestActorScreenSnapPointDistanceX <= FAvaSnapOperation::MaximumSnapDistance);
bSnappedToLinkY = (ClosestActorScreenSnapPointIdxY != INDEX_NONE && ClosestActorScreenSnapPointDistanceY >= 0 && ClosestActorScreenSnapPointDistanceY <= FAvaSnapOperation::MaximumSnapDistance);
#Loc: <Workspace>/Engine/Plugins/Experimental/FloatingProperties/Source/FloatingProperties/Private/Data/FloatingPropertiesSnapMetrics.cpp:33
Scope (from outer to inner):
file
function void FFloatingPropertiesSnapMetrics::SnapToDraggableArea
Source code excerpt:
void FFloatingPropertiesSnapMetrics::SnapToDraggableArea(const FVector2f& InDraggableArea, FVector2f& InOutPosition)
{
if (InOutPosition.X <= FFloatingPropertiesSnapMetrics::SnapDistance)
{
InOutPosition.X = 0.f;
}
else if (InOutPosition.X >= (InDraggableArea.X - FFloatingPropertiesSnapMetrics::SnapDistance))
{
InOutPosition.X = InDraggableArea.X;
}
if (InOutPosition.Y <= FFloatingPropertiesSnapMetrics::SnapDistance)
{
InOutPosition.Y = 0.f;
}
else if (InOutPosition.Y >= (InDraggableArea.Y - FFloatingPropertiesSnapMetrics::SnapDistance))
{
InOutPosition.Y = InDraggableArea.Y;
}
}
#Loc: <Workspace>/Engine/Plugins/Experimental/FloatingProperties/Source/FloatingProperties/Private/Data/FloatingPropertiesSnapMetrics.h:17
Scope: file
Source code excerpt:
static constexpr int32 TopLeft = 0;
static constexpr int32 BottomLeft = 1;
static constexpr float SnapDistance = 10.f;
static constexpr float SnapDistanceSq = SnapDistance * SnapDistance;
static constexpr float SnapBreakDistanceSq = SnapDistanceSq * 2.f;
static FFloatingPropertiesSnapMetrics Make(TSharedRef<FFloatingPropertiesPropertyNode> InNode,
const FVector2f& InSnapPosition, EFloatingPropertiesSnapType InAttachType);
static FVector2f GetSnapPosition(TSharedRef<FFloatingPropertiesPropertyNode> InNode, EFloatingPropertiesSnapType InAttachType);
#Loc: <Workspace>/Engine/Plugins/Experimental/PlanarCutPlugin/Source/PlanarCut/Private/GeometryMeshConversion.cpp:770
Scope (from outer to inner):
file
namespace UE
namespace PlanarCut
namespace AugmentedDynamicMesh
function void FillHoles
Source code excerpt:
/// 1. Build a spatial hash of boundary vertices, so we can identify holes even though the mesh is not welded
double SnapDistance = 1e-03;
TPointHashGrid3d<int32> VertHash(SnapDistance * 10, -1);
TMap<int32, int32> CoincidentVerticesMap; // map every vertex in an overlapping cluster to a canonical single vertex for that cluster
TSet<int32> HashedVertices; // track what's already processed
auto AddVertexToHash = [&Mesh, SnapDistance, &VertHash, &CoincidentVerticesMap, &HashedVertices](int32 VID)
{
if (HashedVertices.Contains(VID))
{
return;
}
#Loc: <Workspace>/Engine/Plugins/Experimental/PlanarCutPlugin/Source/PlanarCut/Private/GeometryMeshConversion.cpp:785
Scope (from outer to inner):
file
namespace UE
namespace PlanarCut
namespace AugmentedDynamicMesh
function void FillHoles
lambda-function
Source code excerpt:
FVector3d VPos = Mesh.GetVertex(VID);
TPair<int32, double> Res = VertHash.FindNearestInRadius(VPos, SnapDistance, [&Mesh, &VPos](const int& VID)->double { return FVector3d::DistSquared(VPos, Mesh.GetVertex(VID)); });
if (Res.Key != INDEX_NONE)
{
int32 MapTo = Res.Key;
int32* WasMapped = CoincidentVerticesMap.Find(MapTo);
if (WasMapped)
{
#Loc: <Workspace>/Engine/Plugins/Experimental/PlanarCutPlugin/Source/PlanarCut/Private/GeometryMeshConversion.cpp:3185
Scope (from outer to inner):
file
namespace UE
namespace PlanarCut
function bool FDynamicMeshCollection::SplitIslands
Source code excerpt:
bool FDynamicMeshCollection::SplitIslands(FDynamicMesh3& Source, TArray<FDynamicMesh3>& SeparatedMeshes)
{
double SnapDistance = 1e-03;
TPointHashGrid3d<int> VertHash(SnapDistance * 10, -1);
FDisjointSet VertComponents(Source.MaxVertexID());
// Add Source vertices to hash & disjoint sets
TArray<int> Neighbors;
for (int VID : Source.VertexIndicesItr())
{
FVector3d Pt = Source.GetVertex(VID);
Neighbors.Reset();
VertHash.FindPointsInBall(Pt, SnapDistance, [&Source, Pt](int OtherVID) {return DistanceSquared(Pt, Source.GetVertex(OtherVID)); }, Neighbors);
for (int NbrVID : Neighbors)
{
VertComponents.UnionSequential(VID, NbrVID);
}
VertHash.InsertPointUnsafe(VID, Pt);
}
#Loc: <Workspace>/Engine/Source/Developer/TraceInsights/Private/Insights/ViewModels/MarkersTimingTrack.cpp:350
Scope (from outer to inner):
file
function double FMarkersTimingTrack::Snap
Source code excerpt:
double SnapTime = std::numeric_limits<double>::infinity();
double SnapDistance = std::numeric_limits<double>::infinity();
if (bUseOnlyBookmarks)
{
LogProvider.EnumerateMessages(
Time - SnapTolerance,
Time + SnapTolerance,
[&SnapTime, &SnapDistance, Time, this](const TraceServices::FLogMessageInfo& Message)
{
if (Message.Category == BookmarkCategory)
{
double Distance = FMath::Abs(Message.Time - Time);
if (Distance < SnapDistance)
{
SnapDistance = Distance;
SnapTime = Message.Time;
}
}
});
}
else
#Loc: <Workspace>/Engine/Source/Developer/TraceInsights/Private/Insights/ViewModels/MarkersTimingTrack.cpp:375
Scope (from outer to inner):
file
function double FMarkersTimingTrack::Snap
lambda-function
Source code excerpt:
Time - SnapTolerance,
Time + SnapTolerance,
[&SnapTime, &SnapDistance, Time, this](const TraceServices::FLogMessageInfo& Message)
{
double Distance = FMath::Abs(Message.Time - Time);
if (Distance < SnapDistance)
{
SnapDistance = Distance;
SnapTime = Message.Time;
}
});
}
if (SnapDistance < SnapTolerance)
{
Time = SnapTime;
}
}
return Time;
#Loc: <Workspace>/Engine/Source/Editor/UMGEditor/Private/Extensions/CanvasSlotExtension.cpp:76
Scope: file
Source code excerpt:
// FCanvasSlotExtension
const double SnapDistance = 7.0;
static double DistancePointToLine2D(const FVector2D& LinePointA, const FVector2D& LinePointB, const FVector2D& PointC)
{
FVector2D AB = LinePointB - LinePointA;
FVector2D AC = PointC - LinePointA;
#Loc: <Workspace>/Engine/Source/Editor/UMGEditor/Private/Extensions/CanvasSlotExtension.cpp:749
Scope: file
Source code excerpt:
//TODO Collide against all sides of the arranged geometry.
double Distance = DistancePointToLine2D(PointA, PointB, CollisionPoint);
if ( Distance <= SnapDistance )
{
FVector2D FarthestPoint = FVector2D::Distance(PointA, CollisionPoint) > FVector2D::Distance(PointB, CollisionPoint) ? PointA : PointB;
FVector2D NearestPoint = FVector2D::Distance(PointA, CollisionPoint) > FVector2D::Distance(PointB, CollisionPoint) ? PointB : PointA;
LinePoints[0] = FarthestPoint;
LinePoints[1] = FarthestPoint + ( NearestPoint - FarthestPoint ) * 100000;
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Classes/Settings/LevelEditorViewportSettings.h:475
Scope (from outer to inner):
file
class class ULevelEditorViewportSettings : public UObject
Source code excerpt:
UPROPERTY(config)
float SnapDistance;
UPROPERTY(config)
int32 CurrentPosGridSize;
UPROPERTY(config)
int32 CurrentRotGridSize;
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/SnappingUtils.cpp:334
Scope (from outer to inner):
file
function bool FEditorViewportSnapping::SnapToBSPVertex
Source code excerpt:
FVector3f DestPoint;
int32 Temp;
if( GWorld->GetModel()->FindNearestVertex(SrcPoint, DestPoint, GetDefault<ULevelEditorViewportSettings>()->SnapDistance, Temp ) >= 0.0)
{
Location = (FVector)DestPoint;
bSnapped = true;
}
}
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/UnrealEdSrv.cpp:3104
Scope (from outer to inner):
file
function bool UUnrealEdEngine::Exec_Mode
Source code excerpt:
}
FParse::Value( Str, TEXT("SNAPDIST="), GetMutableDefault<ULevelEditorViewportSettings>()->SnapDistance );
//
// Major modes:
//
FEditorModeID EditorMode = FBuiltinEditorModes::EM_None;
#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Public/Chaos/PBDRigidClusteringCollisionParticleAlgo.h:9
Scope (from outer to inner):
file
namespace Chaos
function inline TArray<FVec3> CleanCollisionParticles
Source code excerpt:
const TArray<FVec3>& Vertices,
FAABB3 BBox,
const FReal SnapDistance=(FReal)0.01)
{
const int32 NumPoints = Vertices.Num();
if (NumPoints <= 1)
return TArray<FVec3>(Vertices);
FReal MaxBBoxDim = BBox.Extents().Max();
if (MaxBBoxDim < SnapDistance)
return TArray<FVec3>(&Vertices[0], 1);
BBox.Thicken(FMath::Max(SnapDistance/10, (FReal)(UE_KINDA_SMALL_NUMBER*10))); // 0.001
MaxBBoxDim = BBox.Extents().Max();
const FVec3 PointsCenter = BBox.Center();
TArray<FVec3> Points(Vertices);
// Find coincident vertices. We hash to a grid of fine enough resolution such
#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Public/Chaos/PBDRigidClusteringCollisionParticleAlgo.h:35
Scope (from outer to inner):
file
namespace Chaos
function inline TArray<FVec3> CleanCollisionParticles
Source code excerpt:
int32 NumCoincident = 0;
const int64 Resolution = static_cast<int64>(floor(MaxBBoxDim / FMath::Max(SnapDistance,(FReal)UE_KINDA_SMALL_NUMBER)));
const FReal CellSize = static_cast<FReal>(static_cast<double>(MaxBBoxDim) / static_cast<double>(Resolution));
for (int32 i = 0; i < 2; i++)
{
Redundant.Reset();
OccupiedCells.Reset();
// Shift the grid by 1/2 a grid cell the second iteration so that
#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Public/Chaos/PBDRigidClusteringCollisionParticleAlgo.h:74
Scope (from outer to inner):
file
namespace Chaos
function inline TArray<FVec3> CleanCollisionParticles
Source code excerpt:
inline TArray<FVec3> CleanCollisionParticles(
const TArray<FVec3>& Vertices,
const FReal SnapDistance=(FReal)0.01)
{
if (!Vertices.Num())
{
return TArray<FVec3>();
}
FAABB3 BBox(FAABB3::EmptyAABB());
#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Public/Chaos/PBDRigidClusteringCollisionParticleAlgo.h:85
Scope (from outer to inner):
file
namespace Chaos
function inline TArray<FVec3> CleanCollisionParticles
Source code excerpt:
BBox.GrowToInclude(Pt);
}
return CleanCollisionParticles(Vertices, BBox, SnapDistance);
}
inline TArray<FVec3> CleanCollisionParticles(
FTriangleMesh &TriMesh,
const TArrayView<const FVec3>& Vertices,
const FReal Fraction)