// MIT License - Copyright (c) 2023 wallstop
// Full license text: https://github.com/wallstop/unity-helpers/blob/main/LICENSE
namespace WallstopStudios.UnityHelpers.Core.DataStructure.Adapters
{
using System;
using System.Runtime.CompilerServices;
using System.Text.Json.Serialization;
using Helper;
using ProtoBuf;
using UnityEngine;
///
/// Lightweight alternative to Unity's that caches its hash for efficient dictionary and set usage.
/// Provides implicit conversions to and from Unity's struct so gameplay code can continue to use the familiar API while
/// obtaining stable, allocation-free dictionary keys.
///
///
/// labels = new Dictionary();
/// labels[gridCell] = "SpawnPoint";
/// bool hasSpawn = labels.ContainsKey(new FastVector2Int(3, 5));
/// Vector2Int unityVector = gridCell;
/// ]]>
///
[Serializable]
[ProtoContract]
public readonly struct FastVector2Int
: IEquatable,
IEquatable,
IEquatable,
IEquatable,
IComparable,
IComparable,
IComparable,
IComparable,
IComparable
{
///
/// Represents the origin vector (0, 0), useful as a default value without reallocation.
///
///
///
/// FastVector2Int startCell = FastVector2Int.zero;
///
///
public static readonly FastVector2Int zero = new(0, 0);
[ProtoMember(1)]
[JsonIgnore]
public readonly int x;
[ProtoMember(2)]
[JsonIgnore]
public readonly int y;
[ProtoMember(3)]
private readonly int _hash;
///
/// Initializes a new fast vector with integer components and a cached hash.
///
/// The X component.
/// The Y component.
///
///
/// FastVector2Int waypoint = new FastVector2Int(12, -3);
///
///
[JsonConstructor]
public FastVector2Int(int x, int y)
{
this.x = x;
this.y = y;
_hash = Objects.HashCode(x, y);
}
///
/// Gets the stored X component.
///
///
///
/// int column = waypoint.X;
///
///
[JsonPropertyName("x")]
public int X => x;
///
/// Gets the stored Y component.
///
///
///
/// int row = waypoint.Y;
///
///
[JsonPropertyName("y")]
public int Y => y;
///
/// Determines whether two fast vectors are equal by comparing their components.
///
/// The left-hand vector.
/// The right-hand vector.
/// true when both vectors have matching components.
///
///
/// bool sameCell = FastVector2Int.zero == new FastVector2Int(0, 0);
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(FastVector2Int lhs, FastVector2Int rhs)
{
return lhs.Equals(rhs);
}
///
/// Determines whether two fast vectors differ in any component.
///
/// The left-hand vector.
/// The right-hand vector.
/// true when the vectors are not equal.
///
///
/// bool hasMoved = currentCell != previousCell;
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(FastVector2Int lhs, FastVector2Int rhs)
{
return !lhs.Equals(rhs);
}
///
/// Converts a fast vector into Unity's .
///
/// The fast vector to convert.
/// A Unity vector with identical components.
///
///
/// Vector2Int unityVector = FastVector2Int.zero;
///
///
public static implicit operator Vector2Int(FastVector2Int vector)
{
return new Vector2Int(vector.x, vector.y);
}
///
/// Converts a into a by discarding the Z component.
///
/// The three-dimensional fast vector.
/// A two-dimensional fast vector containing the X and Y components.
///
///
/// FastVector2Int planar = new FastVector3Int(2, 7, 4);
///
///
public static implicit operator FastVector2Int(FastVector3Int vector)
{
return new FastVector2Int(vector.x, vector.y);
}
///
/// Converts a Unity to a fast vector while caching its hash code.
///
/// The Unity vector to convert.
/// A new fast vector with identical components.
///
///
/// FastVector2Int cached = new Vector2Int(5, 9);
///
///
public static implicit operator FastVector2Int(Vector2Int vector)
{
return new FastVector2Int(vector.x, vector.y);
}
///
/// Adds two fast vectors component-wise.
///
/// The first summand.
/// The second summand.
/// The component-wise sum.
///
///
/// FastVector2Int destination = origin + new FastVector2Int(1, 0);
///
///
public static FastVector2Int operator +(FastVector2Int lhs, FastVector2Int rhs)
{
return new FastVector2Int(lhs.x + rhs.x, lhs.y + rhs.y);
}
///
/// Subtracts one fast vector from another component-wise.
///
/// The minuend.
/// The subtrahend.
/// The component-wise difference.
///
///
/// FastVector2Int offset = target - origin;
///
///
public static FastVector2Int operator -(FastVector2Int lhs, FastVector2Int rhs)
{
return new FastVector2Int(lhs.x - rhs.x, lhs.y - rhs.y);
}
///
/// Determines whether this fast vector equals another fast vector instance.
///
/// The other fast vector.
/// true when both vectors have identical components.
///
///
/// bool isOrigin = position.Equals(FastVector2Int.zero);
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(FastVector2Int other)
{
return GetHashCode() == other.GetHashCode() && x == other.x && y == other.y;
}
///
/// Determines whether this fast vector equals a Unity .
///
/// The Unity vector.
/// true when the X and Y components match.
///
///
/// bool matches = position.Equals(new Vector2Int(8, 3));
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Vector2Int other)
{
return x == other.x && y == other.y;
}
///
/// Compares this fast vector to another fast vector using lexicographical ordering.
///
/// The other fast vector.
/// A signed integer describing the ordering.
///
///
/// bool isBefore = current.CompareTo(target) < 0;
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CompareTo(FastVector2Int other)
{
int comparison = x.CompareTo(other.x);
return comparison != 0 ? comparison : y.CompareTo(other.y);
}
///
/// Compares this fast vector to a Unity using lexicographical ordering.
///
/// The Unity vector.
/// A signed integer describing the ordering.
///
///
/// bool comesAfter = current.CompareTo(new Vector2Int(1, 2)) > 0;
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CompareTo(Vector2Int other)
{
int comparison = x.CompareTo(other.x);
return comparison != 0 ? comparison : y.CompareTo(other.y);
}
///
/// Determines whether this fast vector equals a , ignoring the Z component.
///
/// The other fast vector.
/// true when the X and Y components match.
///
///
/// bool overlaps = position.Equals(new FastVector3Int(4, 2, 9));
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(FastVector3Int other)
{
return x == other.x && y == other.y;
}
///
/// Compares this fast vector to a by X then Y, ignoring Z.
///
/// The three-dimensional fast vector.
/// A signed integer describing the ordering.
///
///
/// bool precedes = current.CompareTo(new FastVector3Int(4, 2, 1)) < 0;
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CompareTo(FastVector3Int other)
{
int comparison = x.CompareTo(other.x);
return comparison != 0 ? comparison : y.CompareTo(other.y);
}
///
/// Determines whether this fast vector equals a Unity without considering its Z component.
///
/// The Unity vector.
/// true when the planar components match.
///
///
/// bool sharesCell = position.Equals(new Vector3Int(4, 2, 6));
///
///
public bool Equals(Vector3Int other)
{
return x == other.x && y == other.y;
}
///
/// Compares this fast vector to a Unity by X then Y components.
///
/// The Unity vector.
/// A signed integer describing the ordering.
///
///
/// bool isLower = current.CompareTo(new Vector3Int(2, 3, 0)) < 0;
///
///
public int CompareTo(Vector3Int other)
{
int comparison = x.CompareTo(other.x);
if (comparison != 0)
{
return comparison;
}
return y.CompareTo(other.y);
}
///
/// Determines equality against any supported vector representation.
///
/// The candidate vector.
/// true when represents the same planar coordinates.
///
///
/// object candidate = new Vector2Int(2, 1);
/// bool matches = position.Equals(candidate);
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
return obj switch
{
FastVector2Int vector => Equals(vector),
Vector2Int vector => Equals(vector),
FastVector3Int vector => Equals(vector),
Vector3Int vector => Equals(vector),
_ => false,
};
}
///
/// Returns the cached hash code so this vector can be used as a deterministic key in dictionaries and sets.
///
/// An integer hash that combines the X and Y components.
///
/// visitedCells = new HashSet();
/// FastVector2Int cell = new FastVector2Int(2, 4);
/// visitedCells.Add(cell);
/// int hash = cell.GetHashCode();
/// bool contains = visitedCells.Contains(new FastVector2Int(2, 4));
/// ]]>
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return _hash;
}
///
/// Formats the vector as a readable string containing its X and Y components.
///
/// A string in the form (x, y).
///
///
///
public override string ToString()
{
return $"({x}, {y})";
}
///
/// Compares this fast vector with any supported vector representation.
///
/// The candidate vector.
/// A signed integer describing the ordering, or -1 when the type is unsupported.
///
///
/// int ordering = position.CompareTo((object)new Vector2Int(4, 2));
///
///
public int CompareTo(object obj)
{
return obj switch
{
FastVector2Int vector => CompareTo(vector),
Vector2Int vector => CompareTo(vector),
FastVector3Int vector => CompareTo(vector),
Vector3Int vector => CompareTo(vector),
_ => -1,
};
}
///
/// Creates a from this vector with a zero Z component.
///
/// The extended fast vector.
///
///
/// FastVector3Int elevated = position.AsFastVector3Int();
///
///
public FastVector3Int AsFastVector3Int()
{
return new FastVector3Int(x, y);
}
///
/// Converts this fast vector to a Unity .
///
/// A with the same components.
///
///
/// Vector2 uiPosition = position.AsVector2();
///
///
public Vector2 AsVector2()
{
return new Vector2(x, y);
}
///
/// Converts this fast vector to a Unity with a zero Z component.
///
/// A that represents the same planar location.
///
///
/// Vector3 worldPoint = position.AsVector3();
///
///
public Vector3 AsVector3()
{
return new Vector3(x, y);
}
}
}