Code/General/Vector3Extensions.cs
using ExtendedBox.General;
using ExtendedBox.Maths;
using Sandbox;
using System;
using System.Runtime.CompilerServices;

namespace ExtendedBox.General;

public static class Vector3Extensions
{
    extension(Vector3)
    {
        /// <summary>
        /// Calculates signed angle between two vectors based at look direction
        /// <para>
        /// Positive when rotation is counterclockwise<br />
        /// Negative otherwise
        /// </para>
        /// </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <param name="lookDirection"></param>
        public static float GetSignedAngle(Vector3 from, Vector3 to, Vector3 lookDirection)
        {
            float angle = Vector3.GetAngle(from, to);
            Vector3 cross = Vector3.Cross(from, to);
            float sign = Vector3.Dot(lookDirection, cross);
            return sign >= 0 ? angle : -angle;
        }
    }

    public static Vector3 ProjectOnPlane(this Vector3 vector, Vector3 planeNormal) => Vector3.VectorPlaneProject(vector, planeNormal);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static float DistanceSquared(this Vector3Int vector, Vector3 target) => target.DistanceSquared(vector);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static float DistanceSquared(this Vector3Int vector, Vector3Int target) => ((Vector3)target).DistanceSquared(vector);

    public static Vector3Int FloorToInt(this Vector3 vector) =>
        new(vector.x.FloorToInt(), vector.y.FloorToInt(), vector.z.FloorToInt());

    public static Vector3Int CeilToInt(this Vector3 vector) =>
        new(vector.x.CeilToInt(), vector.y.CeilToInt(), vector.z.CeilToInt());

    public static Vector3Int RoundToInt(this Vector3 vector) =>
        new(vector.x.RoundToInt(), vector.y.RoundToInt(), vector.z.RoundToInt());


    public static (Vector3 min, Vector3 max) ComponentMinMax(this Vector3 vector, Vector3 other) =>
        (vector.ComponentMin(other), vector.ComponentMax(other));

    public static (Vector3Int min, Vector3Int max) ComponentMinMax(this Vector3Int vector, Vector3Int other) =>
        (vector.ComponentMin(other), vector.ComponentMax(other));

    public static Vector3 SnapToGrid(this Vector3Int vector, float gridSize, bool sx = true, bool sy = true, bool sz = true) =>
        ((Vector3)vector).SnapToGrid(gridSize, sx, sy, sz);

    public static bool AlmostEqual(this Vector3Int vector, Vector3 other, float delta = 0.0001f)
    {
        if(Math.Abs(vector.x - other.x) > delta)
            return false;

        if(Math.Abs(vector.y - other.y) > delta)
            return false;

        if(Math.Abs(vector.z - other.z) > delta)
            return false;

        return true;
    }


    public static string ToString(this Vector3 vector, string? format)
    {
        return $"{vector.x.ToString(format)},{vector.y.ToString(format)},{vector.z.ToString(format)}";
    }

    public static string ToString(this Vector3 vector, string? format, IFormatProvider? formatProvider)
    {
        return $"{vector.x.ToString(format, formatProvider)},{vector.y.ToString(format, formatProvider)},{vector.z.ToString(format, formatProvider)}";
    }

    public static string ToString(this Vector3 vector, IFormatProvider? formatProvider)
    {
        return $"{vector.x.ToString(formatProvider)},{vector.y.ToString(formatProvider)},{vector.z.ToString(formatProvider)}";
    }

    public static string ToString(this Vector3Int vector, string? format)
    {
        return $"{vector.x.ToString(format)},{vector.y.ToString(format)},{vector.z.ToString(format)}";
    }

    public static string ToString(this Vector3Int vector, string? format, IFormatProvider? formatProvider)
    {
        return $"{vector.x.ToString(format, formatProvider)},{vector.y.ToString(format, formatProvider)},{vector.z.ToString(format, formatProvider)}";
    }

    public static string ToString(this Vector3Int vector, IFormatProvider? formatProvider)
    {
        return $"{vector.x.ToString(formatProvider)},{vector.y.ToString(formatProvider)},{vector.z.ToString(formatProvider)}";
    }
}