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:

  1. Avalanche Viewport plugin
  2. Floating Properties plugin
  3. Planar Cut plugin
  4. Trace Insights system
  5. UMG Editor
  6. Level Editor
  7. Unreal Editor
  8. 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:

  1. The appropriate value for SnapDistance can vary depending on the scale and precision requirements of the specific use case.
  2. Using too large a SnapDistance can result in unintended snapping or merging of elements that should remain separate.
  3. Using too small a SnapDistance might fail to snap elements that should be aligned.

Best practices when using this variable include:

  1. Adjusting the SnapDistance based on the scale of your project and the specific needs of each system using it.
  2. Consider providing user-configurable options for SnapDistance in editor tools to allow for fine-tuning.
  3. Be consistent in how SnapDistance is applied across related systems to ensure predictable behavior.
  4. 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]

#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)