using Sandbox; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Text.Json.Serialization; [JsonConverter( typeof( Sandbox.Internal.JsonConvert.Vector2IntConverter ) )] [StructLayout( LayoutKind.Sequential )] public struct Vector2Int : IEquatable, IParsable { /// /// The X component of this integer vector. /// [ActionGraphInclude( AutoExpand = true )] [JsonInclude] public int x; /// /// The Y component of this integer vector. /// [ActionGraphInclude( AutoExpand = true )] [JsonInclude] public int y; /// /// Initializes an integer vector with given components. /// /// The X component. /// The Y component. [ActionGraphNode( "vec2int.new" ), Title( "Vector2Int" ), Group( "Math/Geometry/Vector2Int" )] public Vector2Int( int x, int y ) { this.x = x; this.y = y; } /// /// Initializes an integer vector with all components set to the same value. /// /// The value of the X and Y components. public Vector2Int( int all = 0 ) : this( all, all ) { } /// /// Initializes an integer vector with given components from another integer vector. /// public Vector2Int( Vector2Int vector2Int ) { x = vector2Int.x; y = vector2Int.y; } /// /// Initializes an integer vector with given components from another integer vector, discarding the Z component. /// public Vector2Int( Vector3Int vector3Int ) { x = vector3Int.x; y = vector3Int.y; } /// /// An integer vector with all components set to 1. /// public static readonly Vector2Int One = new( 1 ); /// /// An integer vector with all components set to 0. /// public static readonly Vector2Int Zero = new( 0 ); /// /// An integer vector with X set to 1. This represents the right direction. /// public static readonly Vector2Int Right = new( 1, 0 ); /// /// An integer vector with X set to -1. This represents the left direction. /// public static readonly Vector2Int Left = new( -1, 0 ); /// /// An integer vector with Y set to 1. This represents the up direction. /// public static readonly Vector2Int Up = new( 0, -1 ); /// /// An integer vector with Y set to -1. This represents the down direction. /// public static readonly Vector2Int Down = new( 0, 1 ); /// /// Formats the integer vector as a string "x,y". /// /// public override string ToString() { return $"{x},{y}"; } /// /// Returns a unit version of this vector. Keep in mind this returns a Vector2 and not a Vector2Int. /// public readonly Vector2 Normal { get { if ( IsZeroLength ) return this; return (Vector2)this / Length; } } /// /// Return the angle of this vector in degrees, always between 0 and 360. /// public readonly float Degrees => System.MathF.Atan2( x, -y ).RadianToDegree().NormalizeDegrees(); // Vector2Int x Number Operators public static Vector2Int operator +( Vector2Int c1, int c2 ) { return new Vector2Int( c1.x + c2, c1.y + c2 ); } public static Vector2 operator +( Vector2Int c1, float c2 ) { return new Vector2( c1.x + c2, c1.y + c2 ); } public static Vector2Int operator -( Vector2Int c1, int c2 ) { return new Vector2Int( c1.x - c2, c1.y - c2 ); } public static Vector2 operator -( Vector2Int c1, float c2 ) { return new Vector2( c1.x - c2, c1.y - c2 ); } public static Vector2Int operator *( Vector2Int c1, int c2 ) { return new Vector2Int( c1.x * c2, c1.y * c2 ); } public static Vector2 operator *( Vector2Int c1, float c2 ) { return new Vector2( c1.x * c2, c1.y * c2 ); } public static Vector2Int operator /( Vector2Int c1, int c2 ) { return new Vector2Int( c1.x / c2, c1.y / c2 ); } public static Vector2 operator /( Vector2Int c1, float c2 ) { return new Vector2( c1.x / c2, c1.y / c2 ); } // Vector2Int x Vector2Int Operators public static Vector2Int operator +( Vector2Int c1, Vector2Int c2 ) { return new Vector2Int( c1.x + c2.x, c1.y + c2.y ); } public static Vector2Int operator -( Vector2Int c1, Vector2Int c2 ) { return new Vector2Int( c1.x - c2.x, c1.y - c2.y ); } public static Vector2Int operator /( Vector2Int c1, Vector2Int c2 ) { return new Vector2Int( c1.x / c2.x, c1.y / c2.y ); } public static Vector2Int operator *( Vector2Int c1, Vector2Int c2 ) { return new Vector2Int( c1.x * c2.x, c1.y * c2.y ); } // Vector2Int x Vector2 Operators public static Vector2 operator +( Vector2Int c1, Vector2 c2 ) { return new Vector2( c1.x + c2.x, c1.y + c2.y ); } public static Vector2 operator +( Vector2 c1, Vector2Int c2 ) { return new Vector2( c1.x + c2.x, c1.y + c2.y ); } public static Vector2 operator -( Vector2 c1, Vector2Int c2 ) { return new Vector2( c1.x - c2.x, c1.y - c2.y ); } public static Vector2 operator -( Vector2Int c1, Vector2 c2 ) { return new Vector2( c1.x - c2.x, c1.y - c2.y ); } public static Vector2 operator *( Vector2 c1, Vector2Int c2 ) { return new Vector2( c1.x * c2.x, c1.y * c2.y ); } public static Vector2 operator *( Vector2Int c1, Vector2 c2 ) { return new Vector2( c1.x * c2.x, c1.y * c2.y ); } public static Vector2 operator /( Vector2Int c1, Vector2 c2 ) { return new Vector2( c1.x / c2.x, c1.y / c2.y ); } public static Vector2 operator /( Vector2 c1, Vector2Int c2 ) { return new Vector2( c1.x / c2.x, c1.y / c2.y ); } // Vector2Int x Vector3 Operators public static Vector3 operator +( Vector3 c1, Vector2Int c2 ) { return new Vector3( c1.x + c2.x, c1.y + c2.y, c1.z ); } public static Vector3 operator +( Vector2Int c1, Vector3 c2 ) { return new Vector3( c1.x + c2.x, c1.y + c2.y, c2.z ); } public static Vector3 operator -( Vector2Int c1, Vector3 c2 ) { return new Vector3( c1.x - c2.x, c1.y - c2.y, -c2.z ); } public static Vector3 operator -( Vector3 c1, Vector2Int c2 ) { return new Vector3( c1.x - c2.x, c1.y - c2.y, c1.z ); } // Vector2Int x Vector4 Operators public static Vector4 operator +( Vector2Int c1, Vector4 c2 ) { return new Vector4( c1.x + c2.x, c1.y + c2.y, c2.z, c2.w ); } public static Vector4 operator +( Vector4 c1, Vector2Int c2 ) { return new Vector4( c1.x + c2.x, c1.y + c2.y, c1.z, c1.w ); } public static Vector4 operator -( Vector2Int c1, Vector4 c2 ) { return new Vector4( c1.x - c2.x, c1.y - c2.y, -c2.z, -c2.w ); } public static Vector4 operator -( Vector4 c1, Vector2Int c2 ) { return new Vector4( c1.x - c2.x, c1.y - c2.y, c1.z, c1.w ); } public static Vector4 operator *( Vector2Int c1, Vector4 c2 ) { return new Vector4( c1.x * c2.x, c1.y * c2.y, c2.z, c2.w ); } public static Vector4 operator *( Vector4 c1, Vector2Int c2 ) { return new Vector4( c1.x * c2.x, c1.y * c2.y, c1.z, c1.w ); } public static Vector4 operator /( Vector2Int c1, Vector4 c2 ) { return new Vector4( c1.x / c2.x, c1.y / c2.y, c2.z, c2.w ); } public static Vector4 operator /( Vector4 c1, Vector2Int c2 ) { return new Vector4( c1.x / c2.x, c1.y / c2.y, c1.z, c1.w ); } // Other Operators public static Vector2Int operator -( Vector2Int c1 ) { return new Vector2Int( -c1.x, -c1.y ); } // Implicit conversions static public implicit operator Vector2Int( int value ) { return new Vector2Int( value, value ); } static public implicit operator Vector2( Vector2Int value ) { return new Vector2( value.x, value.y ); } // Explicit conversions static public explicit operator Vector2Int( Vector2 value ) { return new Vector2Int( (int)value.x, (int)value.y ); } static public explicit operator Vector2Int( Vector3 value ) { return new Vector2Int( (int)value.x, (int)value.y ); } static public explicit operator Vector2Int( Vector4 value ) { return new Vector2Int( (int)value.x, (int)value.y ); } public int this[int index] { get { return index switch { 0 => x, 1 => y, _ => throw new IndexOutOfRangeException(), }; } set { switch ( index ) { case 0: x = value; break; case 1: y = value; break; } } } /// /// Length (or magnitude) of the integer vector (Distance from 0,0) /// public readonly float Length => MathF.Sqrt( x * x + y * y ); /// /// Squared length of the integer vector. This is faster than Length, and can be used for things like comparing distances, as long as only squared values are used."/> /// public readonly int LengthSquared => x * x + y * y; /// /// Returns an integer vector that runs perpendicular to this one. /// public readonly Vector2Int Perpendicular => new Vector2Int( -y, x ); /// /// Whether the length of this vector is zero or not. /// public readonly bool IsZeroLength => x == 0 && y == 0; /// /// Returns true if value on every axis is less than or equal to tolerance /// public readonly bool IsNearlyZero( int tolerance = 0 ) { return Math.Abs( x ) <= tolerance && Math.Abs( y ) <= tolerance; } public void Write( BinaryWriter writer ) { writer.Write( x ); writer.Write( y ); } public readonly Vector2Int Read( BinaryReader reader ) { return new Vector2Int( reader.ReadInt32(), reader.ReadInt32() ); } /// /// Returns an integer vector that has the minimum values on each axis of the two input vectors. /// public readonly Vector2Int ComponentMin( Vector2Int other ) { return new Vector2Int( Math.Min( x, other.x ), Math.Min( y, other.y ) ); } /// /// Returns an integer vector that has the maximum values on each axis of the two input vectors. /// public readonly Vector2Int ComponentMax( Vector2Int other ) { return new Vector2Int( Math.Max( x, other.x ), Math.Max( y, other.y ) ); } /// /// Returns the distance between this vector and another. /// public float Distance( Vector2Int other ) { var dx = other.x - x; var dy = other.y - y; return MathF.Sqrt( dx * dx + dy * dy ); } /// /// Returns the distance between this vector and another. /// public float Distance( Vector2 other ) { var dx = other.x - x; var dy = other.y - y; return MathF.Sqrt( dx * dx + dy * dy ); } /// /// Snap to grid along any of the 2 axes. /// public readonly Vector2Int SnapToGrid( int gridSize, bool sx = true, bool sy = true ) { return new Vector2Int( sx ? x.SnapToGrid( gridSize ) : x, sy ? y.SnapToGrid( gridSize ) : y ); } /// /// Returns an integer vector that has the minimum values on each axis between 2 given vectors. /// public static Vector2Int Min( Vector2Int a, Vector2Int b ) => a.ComponentMin( b ); /// /// Returns an integer vector that has the maximum values on each axis between 2 given vectors. /// public static Vector2Int Max( Vector2Int a, Vector2Int b ) => a.ComponentMax( b ); /// /// Returns a new integer vector with all values positive. -5 becomes 5, ect. /// public readonly Vector2Int Abs() { return new Vector2Int( Math.Abs( x ), Math.Abs( y ) ); } /// /// Returns this integer vector with given X component. /// public readonly Vector2Int WithX( int x ) => new( x, y ); /// /// Returns this integer vector with given Y component. /// public readonly Vector2Int WithY( int y ) => new( x, y ); #region Equality public static bool operator ==( Vector2Int left, Vector2Int right ) => left.Equals( right ); public static bool operator !=( Vector2Int left, Vector2Int right ) => !(left == right); public override bool Equals( object obj ) => obj is Vector2Int o && Equals( o ); public bool Equals( Vector2Int o ) => x == o.x && y == o.y; public override int GetHashCode() => HashCode.Combine( x, y ); #endregion /// /// Given a string, try to convert this into a Vector2Int. Example formatting is "x,y", "[x,y]", "x y", etc. /// public static Vector2Int Parse( string str ) { if ( TryParse( str, CultureInfo.InvariantCulture, out var res ) ) return res; return default; } /// public static Vector2Int Parse( string str, IFormatProvider provider ) { return Parse( str ); } /// public static bool TryParse( [NotNullWhen( true )] string str, IFormatProvider info, [MaybeNullWhen( false )] out Vector2Int result ) { result = Vector2Int.Zero; if ( string.IsNullOrWhiteSpace( str ) ) return false; str = str.Trim( '[', ']', ' ', '\n', '\r', '\t', '"' ); var components = str.Split( new[] { ' ', ',', ';', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries ); if ( components.Length != 2 ) return false; if ( !int.TryParse( components[0], NumberStyles.Integer, info, out int x ) || !int.TryParse( components[1], NumberStyles.Integer, info, out int y ) ) { return false; } result = new Vector2Int( x, y ); return true; } }