// 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 to accelerate dictionary and set lookups.
/// Converts seamlessly to and from Unity vectors so you can drop it into serialization-friendly containers without refactors.
///
///
/// heatMap = new Dictionary();
/// heatMap[voxel] = 0.75f;
/// bool isTracked = heatMap.ContainsKey(new FastVector3Int(4, 2, 6));
/// Vector3Int unityVector = voxel;
/// ]]>
///
[Serializable]
[ProtoContract]
public readonly struct FastVector3Int
: IEquatable,
IEquatable,
IEquatable,
IEquatable,
IComparable,
IComparable,
IComparable,
IComparable,
IComparable
{
///
/// Represents the origin vector (0, 0, 0), useful as a default without allocating new instances.
///
///
///
/// FastVector3Int origin = FastVector3Int.zero;
///
///
public static readonly FastVector3Int zero = new(0, 0, 0);
[ProtoMember(1)]
[JsonIgnore]
public readonly int x;
[ProtoMember(2)]
[JsonIgnore]
public readonly int y;
[ProtoMember(4)]
[JsonIgnore]
public readonly int z;
// Out of order proto is expected
[ProtoMember(3)]
private readonly int _hash;
///
/// Initializes a fast vector with explicit components and a cached hash.
///
/// The X component.
/// The Y component.
/// The Z component.
///
///
/// FastVector3Int gridPosition = new FastVector3Int(12, -3, 5);
///
///
[JsonConstructor]
public FastVector3Int(int x, int y, int z)
{
this.x = x;
this.y = y;
this.z = z;
_hash = Objects.HashCode(x, y, z);
}
///
/// Initializes a fast vector from a Unity while caching its hash.
///
/// The Unity vector to convert.
///
///
/// FastVector3Int cached = new FastVector3Int(new Vector3Int(2, 7, 1));
///
///
public FastVector3Int(Vector3Int vector)
: this(vector.x, vector.y, vector.z) { }
///
/// Initializes a fast vector with a zero Z component.
///
/// The X component.
/// The Y component.
///
///
/// FastVector3Int planar = new FastVector3Int(3, 4);
///
///
public FastVector3Int(int x, int y)
: this(x, y, 0) { }
///
/// Gets the stored X component.
///
///
///
/// int column = voxel.X;
///
///
[JsonPropertyName("x")]
public int X => x;
///
/// Gets the stored Y component.
///
///
///
/// int row = voxel.Y;
///
///
[JsonPropertyName("y")]
public int Y => y;
///
/// Gets the stored Z component.
///
///
///
/// int level = voxel.Z;
///
///
[JsonPropertyName("z")]
public int Z => z;
///
/// Determines whether two fast vectors have identical components.
///
/// The left-hand vector.
/// The right-hand vector.
/// true when both vectors match.
///
///
/// bool matches = FastVector3Int.zero == new FastVector3Int(0, 0, 0);
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(FastVector3Int lhs, FastVector3Int rhs)
{
return lhs.Equals(rhs);
}
///
/// Determines whether two fast vectors differ.
///
/// The left-hand vector.
/// The right-hand vector.
/// true when the vectors are not equal.
///
///
/// bool changed = current != previous;
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(FastVector3Int lhs, FastVector3Int rhs)
{
return !lhs.Equals(rhs);
}
///
/// Adds two fast vectors component-wise.
///
/// The first summand.
/// The second summand.
/// The component-wise sum.
///
///
/// FastVector3Int destination = origin + new FastVector3Int(1, 0, 0);
///
///
public static FastVector3Int operator +(FastVector3Int lhs, FastVector3Int rhs)
{
return new FastVector3Int(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z);
}
///
/// Adds a Unity to a fast vector component-wise.
///
/// The fast vector.
/// The Unity vector.
/// The component-wise sum.
///
///
/// FastVector3Int snapped = current + new Vector3Int(0, 1, 0);
///
///
public static FastVector3Int operator +(FastVector3Int lhs, Vector3Int rhs)
{
return new FastVector3Int(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z);
}
///
/// Adds a Unity to the planar components of a fast vector.
///
/// The fast vector.
/// The two-dimensional Unity vector.
/// The component-wise sum with Z preserved.
///
///
/// FastVector3Int moved = current + new Vector2Int(2, -1);
///
///
public static FastVector3Int operator +(FastVector3Int lhs, Vector2Int rhs)
{
return new FastVector3Int(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z);
}
///
/// Adds a to the planar components of a fast vector.
///
/// The fast vector.
/// The two-dimensional fast vector.
/// The component-wise sum with Z preserved.
///
///
/// FastVector3Int offset = current + new FastVector2Int(0, 3);
///
///
public static FastVector3Int operator +(FastVector3Int lhs, FastVector2Int rhs)
{
return new FastVector3Int(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z);
}
///
/// Subtracts one fast vector from another component-wise.
///
/// The minuend.
/// The subtrahend.
/// The component-wise difference.
///
///
/// FastVector3Int delta = target - origin;
///
///
public static FastVector3Int operator -(FastVector3Int lhs, FastVector3Int rhs)
{
return new FastVector3Int(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z);
}
///
/// Subtracts a Unity from a fast vector component-wise.
///
/// The fast vector.
/// The Unity vector.
/// The component-wise difference.
///
///
/// FastVector3Int offset = current - new Vector3Int(0, 1, 0);
///
///
public static FastVector3Int operator -(FastVector3Int lhs, Vector3Int rhs)
{
return new FastVector3Int(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z);
}
///
/// Subtracts a from the planar components of a fast vector.
///
/// The fast vector.
/// The two-dimensional fast vector.
/// The component-wise difference with Z preserved.
///
///
/// FastVector3Int planarDelta = current - new FastVector2Int(5, 1);
///
///
public static FastVector3Int operator -(FastVector3Int lhs, FastVector2Int rhs)
{
return new FastVector3Int(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z);
}
///
/// Subtracts a Unity from the planar components of a fast vector.
///
/// The fast vector.
/// The two-dimensional Unity vector.
/// The component-wise difference with Z preserved.
///
///
/// FastVector3Int adjustment = current - new Vector2Int(3, 0);
///
///
public static FastVector3Int operator -(FastVector3Int lhs, Vector2Int rhs)
{
return new FastVector3Int(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z);
}
///
/// Converts a fast vector into a Unity .
///
/// The fast vector.
/// A Unity vector with identical components.
///
///
/// Vector3Int unityVector = FastVector3Int.zero;
///
///
public static implicit operator Vector3Int(FastVector3Int vector)
{
return new Vector3Int(vector.x, vector.y, vector.z);
}
///
/// Projects this fast 3D vector onto the XY plane as a Unity by discarding the Z component.
///
/// The fast vector to project.
/// A containing the X and Y components.
///
///
/// Vector2Int tile = (Vector2Int)new FastVector3Int(5, 9, -2);
///
///
public static implicit operator Vector2Int(FastVector3Int vector)
{
return new Vector2Int(vector.x, vector.y);
}
///
/// Converts a Unity to a fast vector while caching its hash.
///
/// The Unity vector.
/// A fast vector with identical components.
///
///
/// FastVector3Int cached = new Vector3Int(6, 1, 9);
///
///
public static implicit operator FastVector3Int(Vector3Int vector)
{
return new FastVector3Int(vector.x, vector.y, vector.z);
}
///
/// Converts a fast vector into Unity's .
///
/// The fast vector.
/// A with identical components.
///
///
/// Vector3 worldPoint = FastVector3Int.zero;
///
///
public static implicit operator Vector3(FastVector3Int vector)
{
return new Vector3(vector.x, vector.y, vector.z);
}
///
/// Converts a Unity to a fast vector with a zero Z component.
///
/// The Unity vector.
/// A fast vector containing the planar components.
///
///
/// FastVector3Int elevated = new Vector2Int(1, 2);
///
///
public static implicit operator FastVector3Int(Vector2Int vector)
{
return new FastVector3Int(vector.x, vector.y, 0);
}
///
/// Converts a fast vector into Unity's .
///
/// The fast vector.
/// A with the X and Y components.
///
///
/// Vector2 planar = FastVector3Int.zero;
///
///
public static implicit operator Vector2(FastVector3Int vector)
{
return new Vector2(vector.x, vector.y);
}
///
/// Returns the cached hash code for the vector so it can participate in hash-based collections without recomputing component hashes.
///
/// A deterministic hash based on X, Y, and Z.
///
/// occupied = new HashSet();
/// FastVector3Int anchor = new FastVector3Int(1, 2, 3);
/// occupied.Add(anchor);
/// int hash = anchor.GetHashCode();
/// bool contains = occupied.Contains(new FastVector3Int(1, 2, 3));
/// ]]>
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return _hash;
}
///
/// Determines equality between this fast vector and a Unity by comparing planar components.
///
/// The Unity vector.
/// true when X and Y match.
///
///
/// bool shareCell = current.Equals(new Vector2Int(7, 1));
///
///
public bool Equals(Vector2Int other)
{
return x == other.x && y == 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(5, 0)) > 0;
///
///
public int CompareTo(Vector2Int other)
{
int comparison = x.CompareTo(other.x);
if (comparison != 0)
{
return comparison;
}
return y.CompareTo(other.y);
}
///
/// Compares this vector to a Unity using lexicographical ordering on X, then Y, then Z.
///
/// The Unity vector to compare to.
/// A signed integer describing the ordering relationship.
///
///
/// FastVector3Int platform = new FastVector3Int(0, 4, 2);
/// bool isAbove = platform.CompareTo(new Vector3Int(0, 3, 10)) > 0;
///
///
public int CompareTo(Vector3Int other)
{
int comparison = x.CompareTo(other.x);
if (comparison != 0)
{
return comparison;
}
comparison = y.CompareTo(other.y);
if (comparison != 0)
{
return comparison;
}
return z.CompareTo(other.z);
}
///
/// Determines equality against any supported vector representation.
///
/// The candidate vector.
/// true when represents the same coordinates.
///
///
/// object candidate = new Vector3Int(4, 2, 6);
/// bool matches = current.Equals(candidate);
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
return obj switch
{
FastVector3Int vector => Equals(vector),
Vector3Int vector => Equals(vector),
FastVector2Int vector => Equals(vector),
Vector2Int vector => Equals(vector),
_ => false,
};
}
///
/// Determines equality with a Unity instance.
///
/// The Unity vector.
/// true when all components match.
///
///
/// bool identical = current.Equals(new Vector3Int(1, 2, 3));
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Vector3Int other)
{
return x == other.x && y == other.y && z == other.z;
}
///
/// Determines equality with another fast vector.
///
/// The other fast vector.
/// true when all components match.
///
///
/// bool isOrigin = current.Equals(FastVector3Int.zero);
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(FastVector3Int other)
{
return GetHashCode() == other.GetHashCode()
&& x == other.x
&& y == other.y
&& z == other.z;
}
///
/// Determines equality with a planar by comparing X and Y.
///
/// The two-dimensional fast vector.
/// true when the planar components match.
///
///
/// bool overlaps = current.Equals(new FastVector2Int(5, 3));
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(FastVector2Int other)
{
return x == other.x && y == other.y;
}
///
/// Compares this fast vector to another fast vector using lexicographical ordering by X, then Y, then Z.
///
/// The other fast vector.
/// A signed integer describing the ordering.
///
///
/// bool isBefore = current.CompareTo(target) < 0;
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CompareTo(FastVector3Int other)
{
int xComparison = x.CompareTo(other.x);
if (xComparison != 0)
{
return xComparison;
}
int yComparison = y.CompareTo(other.y);
if (yComparison != 0)
{
return yComparison;
}
return z.CompareTo(other.z);
}
///
/// Compares this fast vector to a by X then Y.
///
/// The two-dimensional fast vector.
/// A signed integer describing the ordering.
///
///
/// bool precedes = current.CompareTo(new FastVector2Int(1, 4)) < 0;
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CompareTo(FastVector2Int other)
{
int comparison = x.CompareTo(other.x);
if (comparison != 0)
{
return comparison;
}
return y.CompareTo(other.y);
}
///
/// Compares this fast vector to any supported vector representation.
///
/// The candidate vector.
/// A signed integer describing the ordering, or -1 when unsupported.
///
///
/// int ordering = current.CompareTo((object)new Vector3Int(8, 0, 2));
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CompareTo(object other)
{
return other switch
{
FastVector3Int vector => CompareTo(vector),
Vector3Int vector => CompareTo(vector),
FastVector2Int vector => CompareTo(vector),
Vector2Int vector => CompareTo(vector),
_ => -1,
};
}
///
/// Formats the vector as a string tuple containing X, Y, and Z.
///
/// A string formatted as (x, y, z).
///
///
/// FastVector3Int checkpoint = new FastVector3Int(-2, 8, 15);
/// string label = checkpoint.ToString();
///
///
public override string ToString()
{
return $"({x}, {y}, {z})";
}
///
/// Converts this vector to a by discarding the Z component.
///
/// The planar fast vector.
///
///
/// FastVector2Int planar = current.FastVector2Int();
///
///
public FastVector2Int FastVector2Int()
{
return new FastVector2Int(x, y);
}
///
/// Converts this fast vector to a Unity .
///
/// The planar .
///
///
/// Vector2 uiPosition = current.AsVector2();
///
///
public Vector2 AsVector2()
{
return new Vector2(x, y);
}
///
/// Converts this fast vector to a Unity .
///
/// The with identical components.
///
///
/// Vector3 worldPoint = current.AsVector3();
///
///
public Vector3 AsVector3()
{
return new Vector3(x, y, z);
}
}
}