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;
}
}