editor/AdvancedMappingTools/Core/SnapManager.cs
namespace AdvancedMappingTools;

public enum SnapMode
{
	[Title( "Editor Grid" )] Editor,
	[Title( "Custom Grid" )] Custom,
	Off
}

/// <summary>
/// Snapping shared by all builders. Points stay at their height, only X and Y move.
/// </summary>
public static class SnapManager
{
	public static Vector3 ToGrid( Vector3 point, SnapMode mode, float gridSize )
	{
		var height = point.z;

		point = mode switch
		{
			SnapMode.Editor => Gizmo.Snap( point, new Vector3( 1, 1, 0 ) ),
			SnapMode.Custom when gridSize > 0.0f => ToCustomGrid( point, gridSize ),
			_ => point
		};

		return point with { z = height };
	}

	public static Vector3 ToCustomGrid( Vector3 point, float gridSize ) => new(
		MathF.Round( point.x / gridSize ) * gridSize,
		MathF.Round( point.y / gridSize ) * gridSize,
		point.z );

	/// <summary>
	/// Lock the direction from <paramref name="origin"/> to steps of <paramref name="step"/> degrees, keeping the distance.
	/// </summary>
	public static Vector3 ToAngle( Vector3 point, Vector3 origin, float step )
	{
		if ( step <= 0.0f ) return point;

		var delta = (point - origin).WithZ( 0.0f );
		var radians = step.DegreeToRadian();
		var angle = MathF.Round( MathF.Atan2( delta.y, delta.x ) / radians ) * radians;

		return new Vector3( origin.x + MathF.Cos( angle ) * delta.Length, origin.y + MathF.Sin( angle ) * delta.Length, point.z );
	}

	/// <summary>
	/// Pull a point onto the nearest corner of a mesh, if one is inside the radius.
	/// </summary>
	public static Vector3 ToVertex( Vector3 point, MeshComponent mesh, float radius )
	{
		if ( !mesh.IsValid() || mesh.Mesh is null ) return point;

		var transform = mesh.WorldTransform;
		var best = radius * radius;
		var result = point;

		foreach ( var local in mesh.Mesh.GetVertexPositions() )
		{
			var world = transform.PointToWorld( local );
			var distance = (world - point).WithZ( 0.0f ).LengthSquared;

			if ( distance >= best ) continue;

			best = distance;
			result = world with { z = point.z };
		}

		return result;
	}
}