Maybe I’m the only one looking for it, but… the absence of Vector4Int when there’s Vector3Int and Vector2Int to match their floating point counterparts, is mildly uncomfortable.
Any plans to include it?
Maybe I’m the only one looking for it, but… the absence of Vector4Int when there’s Vector3Int and Vector2Int to match their floating point counterparts, is mildly uncomfortable.
Any plans to include it?
I think you may be the only one looking for it, TBH. I’m having a hard time imagining a point for Vector4Int. It’s tough enough thinking of ways to use Vector4, and all the examples for that I can think of for that (v3+time, some graphics shenanigans, etc) are very explicitly dependent on the numbers being floats.
Agreed with StarManta, but if you find yourself needing it you can just write your own:
using System;
using System.Globalization;
using System.Runtime.InteropServices;
using UnityEngine.Scripting;
namespace UnityEngine
{
[System.Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Vector4Int : IEquatable<Vector4Int>, IFormattable
{
public int x { get { return m_X; } set { m_X = value; } }
public int y { get { return m_Y; } set { m_Y = value; } }
public int z { get { return m_Z; } set { m_Z = value; } }
public int w { get { return m_W; } set { m_W = value; } }
private int m_X;
private int m_Y;
private int m_Z;
private int m_W;
public Vector4Int(int x, int y, int z, int w)
{
m_X = x;
m_Y = y;
m_Z = z;
m_W = w;
}
// Set x, y and z components of an existing Vector.
public void Set(int x, int y, int z, int w)
{
m_X = x;
m_Y = y;
m_Z = z;
m_W = w;
}
// Access the /x/, /y/ or /z/ component using [0], [1] or [2] respectively.
public int this[int index]
{
get
{
switch (index)
{
case 0: return x;
case 1: return y;
case 2: return z;
case 3: return w;
default:
throw new IndexOutOfRangeException(UnityString.Format("Invalid Vector4Int index addressed: {0}!", index));
}
}
set
{
switch (index)
{
case 0: x = value; break;
case 1: y = value; break;
case 2: z = value; break;
case 3: w = value; break;
default:
throw new IndexOutOfRangeException(UnityString.Format("Invalid Vector4Int index addressed: {0}!", index));
}
}
}
// Returns the length of this vector (RO).
public float magnitude { get { return Mathf.Sqrt((float)(x * x + y * y + z * z + w * w)); } }
// Returns the squared length of this vector (RO).
public int sqrMagnitude { get { return x * x + y * y + z * z + w * w; } }
// Returns the distance between /a/ and /b/.
public static float Distance(Vector4Int a, Vector4Int b) { return (a - b).magnitude; }
// Returns a vector that is made from the smallest components of two vectors.
public static Vector4Int Min(Vector4Int lhs, Vector4Int rhs) { return new Vector4Int(Mathf.Min(lhs.x, rhs.x), Mathf.Min(lhs.y, rhs.y), Mathf.Min(lhs.z, rhs.z), Mathf.Min(lhs.w, rhs.w)); }
// Returns a vector that is made from the largest components of two vectors.
public static Vector4Int Max(Vector4Int lhs, Vector4Int rhs) { return new Vector4Int(Mathf.Max(lhs.x, rhs.x), Mathf.Max(lhs.y, rhs.y), Mathf.Max(lhs.z, rhs.z), Mathf.Max(lhs.w, rhs.w)); }
// Multiplies two vectors component-wise.
public static Vector4Int Scale(Vector4Int a, Vector4Int b) { return new Vector4Int(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); }
// Multiplies every component of this vector by the same component of /scale/.
public void Scale(Vector4Int scale) { x *= scale.x; y *= scale.y; z *= scale.z; w *= scale.w; }
public void Clamp(Vector4Int min, Vector4Int max)
{
x = Math.Max(min.x, x);
x = Math.Min(max.x, x);
y = Math.Max(min.y, y);
y = Math.Min(max.y, y);
z = Math.Max(min.z, z);
z = Math.Min(max.z, z);
w = Math.Max(min.w, w);
w = Math.Min(max.w, w);
}
// Converts a Vector4Int to a [[Vector4]].
public static implicit operator Vector4(Vector4Int v)
{
return new Vector4(v.x, v.y, v.z, v.w);
}
// Converts a Vector4Int to a [[Vector2Int]].
public static explicit operator Vector3Int(Vector4Int v)
{
return new Vector3Int(v.x, v.y, v.z);
}
// Converts a Vector4Int to a [[Vector2Int]].
public static explicit operator Vector2Int(Vector4Int v)
{
return new Vector2Int(v.x, v.y);
}
public static Vector4Int FloorToInt(Vector4 v)
{
return new Vector4Int(
Mathf.FloorToInt(v.x),
Mathf.FloorToInt(v.y),
Mathf.FloorToInt(v.z),
Mathf.FloorToInt(v.w)
);
}
public static Vector4Int CeilToInt(Vector4 v)
{
return new Vector4Int(
Mathf.CeilToInt(v.x),
Mathf.CeilToInt(v.y),
Mathf.CeilToInt(v.z),
Mathf.CeilToInt(v.w)
);
}
public static Vector4Int RoundToInt(Vector4 v)
{
return new Vector4Int(
Mathf.RoundToInt(v.x),
Mathf.RoundToInt(v.y),
Mathf.RoundToInt(v.z),
Mathf.RoundToInt(v.w)
);
}
public static Vector4Int operator+(Vector4Int a, Vector4Int b)
{
return new Vector4Int(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
}
public static Vector4Int operator-(Vector4Int a, Vector4Int b)
{
return new Vector4Int(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
}
public static Vector4Int operator*(Vector4Int a, Vector4Int b)
{
return new Vector4Int(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
}
public static Vector4Int operator-(Vector4Int a)
{
return new Vector4Int(-a.x, -a.y, -a.z, -a.w);
}
public static Vector4Int operator*(Vector4Int a, int b)
{
return new Vector4Int(a.x * b, a.y * b, a.z * b, a.w * b);
}
public static Vector4Int operator*(int a, Vector4Int b)
{
return new Vector4Int(a * b.x, a * b.y, a * b.z, a * b.w);
}
public static Vector4Int operator/(Vector4Int a, int b)
{
return new Vector4Int(a.x / b, a.y / b, a.z / b, a.w / b);
}
public static bool operator==(Vector4Int lhs, Vector4Int rhs)
{
return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z && lhs.w == rhs.w;
}
public static bool operator!=(Vector4Int lhs, Vector4Int rhs)
{
return !(lhs == rhs);
}
public override bool Equals(object other)
{
if (!(other is Vector4Int)) return false;
return Equals((Vector4Int)other);
}
public bool Equals(Vector4Int other)
{
return this == other;
}
public override int GetHashCode()
{
var yHash = y.GetHashCode();
var zHash = z.GetHashCode();
var wHash = w.GetHashCode();
return x.GetHashCode() ^ (yHash << 8) ^ (yHash >> 24) ^ (zHash << 16) ^ (zHash >> 16) ^ (wHash << 24) ^ (wHash >> 8);
}
public override string ToString()
{
return ToString(null, CultureInfo.InvariantCulture.NumberFormat);
}
public string ToString(string format)
{
return ToString(format, CultureInfo.InvariantCulture.NumberFormat);
}
public string ToString(string format, IFormatProvider formatProvider)
{
return UnityString.Format("({0}, {1}, {2}, {3})", x.ToString(format, formatProvider), y.ToString(format, formatProvider), z.ToString(format, formatProvider), w.ToString(format, formatProvider));
}
public static Vector4Int zero { get { return s_Zero; } }
public static Vector4Int one { get { return s_One; } }
private static readonly Vector4Int s_Zero = new Vector4Int(0, 0, 0, 0);
private static readonly Vector4Int s_One = new Vector4Int(1, 1, 1, 1);
}
}
completely untested, written quickly in notepad, based off the unity source for Vector3Int:
I was surprised to see that Vector4 existed; what could it be used for?
4-dimensional vector math.
Specifically in unity things like mesh tangents, and shader logic. Shader logic uses 4d vectors for various things since a lot of the math utilizes a 4x4 matrix for transformations, and a row/col of that matrix will be a 4d vector. Vector4 exists as a type to translate between shaders and unity. Also color information in shaders can be stored as 4d vectors (rgba, argb, etc). As well as when you may just need to do some 4d vector math of your own.
Hi!
I wrote my own simplistic version of it, but it simply struck me as strange that it did not already exist.
My use case is a bit special. Im writing a cell based spatial subdivision structure in which cells are four dimensional. So the index of a cell is 4 ints. Might as well write a “CellIndex” struct specific for this.
Vector4, in addition to matrix multiplication, shaders, storing colors and some unconventional quaternion operations, also comes in handy when interfacing with native code that needs 16 byte alignment for simd intrinsics. I guess i’m a weirdo, but I very often I find myself using Vector4.
As ridiculous as it sounds, technically doing this is a violation of the Unity Reference Only License in which the Unity source is licensed under
https://unity3d.com/legal/licenses/Unity_Reference_Only_License
I absolutely think Unity should include Vector4Int. The argument for consistency should be enough. I am using it for containing values to be uploaded to shaders.
download the mathametics package from package manager
Not a valid replacement for me. However I wrote my own struct that contains 4 ints, as well as some very specific operations over it. All is well
That is a ridiculous statement. Anytime you need to do 4D math you need a 4D vector. It’s also ridiculous that it’s missing and we have to use Vector4 instead.
This is an old thread (warn me and lock it if you dare) but I wanted to use Vector4Int to store data for RectInts, which I’m using to procedurally generate things on a grid. The int is important due to the grid nature. It’s not even needed as I only ever supply it with ints, not floats, but that’s beside the point.
All I had to do was add (int) to the components when I needed to reference them. However I was compelled to add my use case here since a few people mentioned “I can’t imagine why you would need to do that” which was obviously the case with whoever neglected to include it in the first place, and is simply a naive statement as shown by the various people here with interest in Vector4Int or representing various uses for it. There will always be people trying to do different things in different ways. Sure, sometimes maybe it’s because they’re trying to do something an unintendedly bad way and they don’t know any better, but you can always give people the benefit of the doubt.
What data of a RectInt are you storing that can’t be stored in a RectInt?
A RectInt consists of 4 int values… x(min), y(min), width, height. All other values are implied from those 4.
https://github.com/Unity-Technologies/UnityCsReference/blob/6c8a95ff127619e73519662fa497e242b898f9af/Runtime/Export/Geometry/RectInt.cs#L19
What is the Vector4Int doing that the RectInt isn’t already doing?
…
Years ago when StarManta said that they can’t imagine a need for Vector4Int… they weren’t talking about not imagining needing a struct of 4 ints. Sure, a struct of 4 ints could have boundless uses.
And if you need a struct of 4 ints… creating one is trivial:
public struct Int4
{
public int x;
public int y;
public int z;
public int w;
}
They’re referring specifically to the need of a Vector4Int. Because Vector4Int has a greater implied thing. It’s not just a data container (which appears to be your need… you want store some data for some RectInts that RectInt isn’t itself suitable?) But rather is a mathematical concept. It’s… a vector of discrete values. Vectors have certain attributes, namely “magnitude and direction”. It’s the definition of a vector. And from there various mathematical operations are defined (like scalar products, addition and subtraction, distance (which is the magnitude of the vector resulting from a subtraction), and other things.
This is what StarManta is referring to not knowing what you may need a Vector4Int for.
Vector2Int and Vector3Int are specifically intended for positional vectors with only whole values. This makes sense since Unity has 2D and 3D gameplay.
It doesn’t have 4D gameplay.
You might say “well then what is Vector4 for”, but as I said a few years ago that has to do with shader logic and the sort and it behaves as a translation struct between shader code and C#. But there isn’t really a need for int versions there since well shaders are all about float math… not integer math. It’s what GPUs do best… they’re effectively thousands of FPUs (floating point units) running in parallel.
…
Now yeah, some people may come up with edge case scenarios for such things. May it be 4D math related (as some people have suggested). But if you’re out here doing 4D maths for your 4D game… well writing a simple struct to do it is trivial. Especially considering that Vector4Int from Unity wouldn’t have any of the interesting functions you might expect (Vector3Int is fairly basic as well and lacks things like dot products and the sort… so a Vector4Int version would lack such things as well likely and we’d all be back in the boat of “why doesn’t it have this function or that”).
Speaking of which… mathematics has it! And if you’re doing complicated 4d maths you probably are going to want the unity mathematics library.
And sure… maybe all you want is just this one struct and don’t want to import the whole unity mathematics library in just for int4.
Yeah… that’s a good point.
But couldn’t you also argue that why should Unity have to implement Vector4Int in the core library just because a couple people might have this weird edge case of needing it but not needing the unity mathematics library and isn’t otherwise just exploiting it as a basic data storage struct in which case… implementing it yourself is trivial as I showed above in just 7 lines of code.
Wait… I’ll do it in one:
public struct Vector4Int { public int x, y, z, w; }
If all you need is storage…
int4 isn’t good enough?
https://docs.unity3d.com/Packages/com.unity.mathematics@1.3/api/Unity.Mathematics.int4.html
Thank you both for your responses. To address the first thing that came up, I need to make another non-rectint object with the same dimensions so just start with simple data.
As you both pointed out, there are a multitude of ways to accomplish the task. Those are great and appropriate answers.
& Thank You @lordofduct
One UseCase would be to have neat colorValues i.e. in the 0-255 int range of colors, in the code & game & maybe values that in somewayorother will be shown to the user.
It’s just so much nicer to user a color from those
To store them clean and clear as int is much nicer & better if you ever need ‘conversions’ back and forth than floating points.
For that we have Color32 which also uses bytes and not ints to exactly cover that 0-255 range. This is actually a data type that’s used extensively as most color information on the native side is stored as Color32. Many APIs in Unity actually provide two versions like Texture GetPixels and Texture GetPixels32. Of course the Color32 version requires only a quarter of the memory a normal “Color” value uses. Though Color32 can’t represent values outside the 0-255 range of course.
There are conversion operators between Color and Color32, so in many cases they can be used interchangeably. I still don’t see an actual usecase for a Vector4Int. It would have the same size of a Color instance (32 bits per channel) and just provides worse resolution. The only usecase I could think of would be a 4d voxel world. Though that would be a very very rare exception and requires a lot of custom code anyways (probably a Matrix5x5 and Vector5 which I actually made some time ago ).
I’ve made a huge library, comparable to Unity Mathematics (mostly in nomenclature and overall usage) which supports and extends nearly all struct-like types like Vector2/3/4, Quaternion, Color etc. and reimplements the underlying math and other useful operations (like hexadecimal color conversion and so on).
I’ve done it mostly as a congregation of my knowledge over the years, because having deeper understanding about the fundamentals lets me do more math quickly and correctly. It is also a useful reminder of some linear algebra tricks and principles, and a neat collection of optimized code, practical utilities, gems, left-hand rules… Also having internals exposed lets me lower or customize my math code when needed. Finally it makes my math easily convertible to Unity Mathematics (and Burst compiler), and transferrable to and fro the shader mindset, if such a need arises (and it happens so much, that I in fact began to prefer shader code when doing math, as it’s easier to parse).
There I specifically use Vector4 as a transient container in some places, because it lets me do some common math on quaternions (for example) by reusing the existing math intended for Vector4 (i.e. dot and normalization).
All of this to say that I have never encountered a need for Vector4Int in 10+ years of working with Unity. The only argument I can acknowledge could be the “completeness” one, but why would anyone need a discrete 4D vector is beyond me. And if your game is so specific that it warrants a discrete 4D vector, it’s still something you can do in 15 minutes. But I truly believe most people just want a struct with 4 ints.
Here’s how you would do Int3 (admittedly not in 15 minutes), feel free to extend this to four dimensions.
Int3
using System;
namespace DiscreteSpace {
using Vector3 = UnityEngine.Vector3;
using Vector3Int = UnityEngine.Vector3Int;
[Serializable]
public partial struct Int3 : IEquatable<Int3> {
static public readonly Int3 zero = new Int3(0, 0, 0);
static public readonly Int3 right = new Int3(1, 0, 0);
static public readonly Int3 up = new Int3(0, 1, 0);
static public readonly Int3 forward = new Int3(0, 0, 1);
static public readonly Int3 one = new Int3(1, 1, 1);
static public readonly Int3 X = new Int3(1, 0, 0);
static public readonly Int3 Y = new Int3(0, 1, 0);
static public readonly Int3 Z = new Int3(0, 0, 1);
static public readonly Int3 XY = new Int3(1, 1, 0);
static public readonly Int3 YZ = new Int3(0, 1, 1);
static public readonly Int3 XZ = new Int3(1, 0, 1);
public int x;
public int y;
public int z;
public Int3(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
public int this[int index] {
get {
switch(index) {
case 0: return x;
case 1: return y;
case 2: return z;
default: throw new NotSupportedException();
}
}
set {
switch(index) {
case 0: x = value; break;
case 1: y = value; break;
case 2: z = value; break;
default: throw new NotSupportedException();
}
}
}
static public Int3 Cube(int v) => new Int3(v,v,v);
static public Int3 Abs(Int3 p) => new Int3(Math.Abs(p.x), Math.Abs(p.y), Math.Abs(p.z));
static public Int3 Distance(Int3 p1, Int3 p2) => Abs(p2 - p1);
static public Int3 Floor(Vector3 vec) => new Int3( (int)MathF.Floor(vec.x), (int)MathF.Floor(vec.y), (int)MathF.Floor(vec.z));
static public Int3 Round(Vector3 vec) => new Int3( (int)MathF.Round(vec.x), (int)MathF.Round(vec.y), (int)MathF.Round(vec.z));
static public Int3 Ceil(Vector3 vec) => new Int3((int)MathF.Ceiling(vec.x), (int)MathF.Ceiling(vec.y), (int)MathF.Ceiling(vec.z));
/// <summary>
/// Returns the sum of components.
/// </summary>
public int Sum() => x + y + z;
/// <summary>
/// Returns the sum of squared components.
/// </summary>
public int SqrSum() => x * x + y * y + z * z;
/// <summary>
/// Returns the product of components.
/// </summary>
public int Mul() => x * y * z;
/// <summary>
/// Returns the average of components.
/// </summary>
public float Avg() => (float)Sum() / 3f;
/// <summary>
/// Returns the index of the minimum component (if tied the leftmost component is chosen).
/// </summary>
public int IndexOfMin() {
if(x <= y && x <= z) return 0;
if(y <= x && y <= z) return 1;
return 2;
}
/// <summary>
/// Returns the index of the maximum component (if tied the rightmost component is chosen).
/// </summary>
public int IndexOfMax() {
if(z >= y && z >= x) return 2;
if(y >= z && y >= x) return 1;
return 0;
}
/// <summary>
/// Doesn't return the "lesser" of the two, but instead returns the reassembled component-wise minimum.
/// </summary>
static public Int3 LeastOf(Int3 p1, Int3 p2)
=> new Int3(MathF.Min(p1.x, p2.x), MathF.Min(p1.y, p2.y), MathF.Min(p1.z, p2.z));
/// <summary>
/// Doesn't return the "greater" of the two, but instead returns the reassembled component-wise maximum.
/// </summary>
static public Int3 MostOf(Int3 p1, Int3 p2)
=> new Int3(MathF.Max(p1.x, p2.x), MathF.Max(p1.y, p2.y), MathF.Max(p1.z, p2.z));
/// <summary>
/// Returns a point that is confined to a specified box size, component-wise.
/// </summary>
/// <param name="size">Exclusive maximum. Components must be > 0.</param>
public Int3 ClampedTo(Int3 size) {
if(size.x < 0 || size.y < 0 || size.z < 0) throw new ArgumentOutOfRangeException(nameof(size), "Invalid box size.");
var n = this; clamp_impl(ref n, zero, size); return n;
}
/// <summary>
/// Clamps the point so that it's confined to a specified box size, component-wise.
/// </summary>
/// <param name="size">Exclusive maximum. Components must be > 0.</param>
public void Clamp(Int3 size) {
if(size.x < 0 || size.y < 0 || size.z < 0) throw new ArgumentOutOfRangeException(nameof(size), "Invalid box size.");
clamp_impl(ref this, zero, size);
}
/// <summary>
/// Returns a point that is confined to a specified box size, component-wise.
/// </summary>
/// <param name="box">Exclusive maximum. Size must be > 0.</param>
public Int3 ClampedTo(in BoxInt box) {
if(box.Size.x < 0 || box.Size.y < 0 || box.Size.z < 0) throw new ArgumentException("Invalid box size.", nameof(box));
var n = this; clamp_impl(ref n, box.min, box.max); return n;
}
/// <summary>
/// Changes the point so that it's confined to a specified box size, component-wise.
/// </summary>
/// <param name="box">Exclusive maximum. Size must be > 0.</param>
public void Clamp(in BoxInt box) {
if(box.Size.x < 0 || box.Size.y < 0 || box.Size.z < 0) throw new ArgumentException("Invalid box size.", nameof(box));
clamp_impl(ref this, box.min, box.max);
}
static private void clamp_impl(ref Int3 p, Int3 min, Int3 max) {
if(is_within_impl(p, min, max + 1)) return;
p.x = p.x.Clamp(min.x, max.x);
p.y = p.y.Clamp(min.y, max.y);
p.z = p.z.Clamp(min.z, max.z);
}
public Int3 ExpandedBy(int value) => this + value;
public void Expand(int value) { x += value; y += value; z += value; }
public Int3 ShrunkBy(int value) => this - value;
public void Shrink(int value) { x -= value; y -= value; z -= value; }
/// <summary>
/// Returns true if components are >= 0 and < <c>size</c>, false otherwise.
/// </summary>
public bool IsWithin(Int3 size) => is_within_impl(this, zero, size);
/// <summary>
/// Returns true if components are >= <c>min</c> and < <c>max</c>, false otherwise.
/// </summary>
public bool IsWithin(Int3 min, Int3 max) => is_within_impl(this, min, max);
/// <summary>
/// Returns true if components are >= <c>box.min</c> and < <c>box.max</c>, false otherwise.
/// </summary>
public bool IsWithin(in BoxInt box) => is_within_impl(this, box.min, box.max);
static private bool is_within_impl(Int3 p, Int3 min, Int3 max)
=> (p.x >= min.x && p.x < max.x) &&
(p.y >= min.y && p.y < max.y) &&
(p.z >= min.z && p.z < max.z);
static public Int3 Sign(Int3 point) => new Int3(Math.Sign(point.x), Math.Sign(point.y), Math.Sign(point.z));
public delegate int AxisIterator(int axis);
public delegate int AxisValueIterator(int axis, int value);
public delegate bool AxisValueTest(int axis, int value);
public Int3 ForEach(AxisValueIterator lambda, Permutation3D sequence = Permutation3D.XYZ) {
if(lambda is null) return this;
x = lambda(0, x); y = lambda(1, y); z = lambda(2, z);
if(sequence != Permutation3D.XYZ) {
var r = this.Permuted(sequence);
x = r.x; y = r.y; z = r.z;
}
return this;
}
static public Int3 ForEach(AxisIterator lambda, Permutation3D sequence = Permutation3D.XYZ) {
var r = Int3.zero;
if(lambda is null) return r;
r[0] = lambda(0); r[1] = lambda(1); r[2] = lambda(2);
return r.Permuted(sequence);
}
public bool IfAny(AxisValueTest test) {
if(!(test is null) || test(0, x) || test(1, y) || test(2, z)) return true;
return false;
}
public bool IfAll(AxisValueTest test) {
if(test is null || !test(0, x) || !test(1, y) || !test(2, z)) return false;
return true;
}
// unary minus
static public Int3 operator -(Int3 p) => new Int3(-p.x, -p.y, -p.z);
// component-wise addition (and expansion)
static public Int3 operator +(Int3 p1, Int3 p2) => new Int3(p1.x + p2.x, p1.y + p2.y, p1.z + p2.z);
static public Int3 operator +(Int3 p, int i) => new Int3(p.x + i, p.y + i, p.z + i);
// component-wise subtraction (and shrinkage)
static public Int3 operator -(Int3 p1, Int3 p2) => new Int3(p1.x - p2.x, p1.y - p2.y, p1.z - p2.z);
static public Int3 operator -(Int3 p, int i) => new Int3(p.x - i, p.y - i, p.z - i);
// component-wise multiplication
static public Int3 operator *(Int3 p1, Int3 p2) => new Int3(p1.x * p2.x, p1.y * p2.y, p1.z * p2.z);
// scalar multiplication
static public Int3 operator *(Int3 p, int i) => i * p;
static public Int3 operator *(int i, Int3 p) => new Int3(i * p.x, i * p.y, i * p.z);
// scalar integer division
static public Int3 operator /(Int3 p, int i) => new Int3(p.x / i, p.y / i, p.z / i);
// scalar modulo
static public Int3 operator %(Int3 p, int i) => new Int3(p.x % i, p.y % i, p.z % i);
// unary bitwise negation (NOT)
static public Int3 operator ~(Int3 p) => new Int3(~p.x, ~p.y, ~p.z);
// bitwise shift left
static public Int3 operator <<(Int3 p, int bits) => new Int3(p.x << bits, p.y << bits, p.z << bits);
// bitwise shift right
static public Int3 operator >>(Int3 p, int bits) => new Int3(p.x >> bits, p.y >> bits, p.z >> bits);
// bitwise conjunction (AND)
static public Int3 operator &(Int3 p1, Int3 p2) => new Int3(p1.x & p2.x, p1.y & p2.y, p1.z & p2.z);
static public Int3 operator &(Int3 p, int rhs) => new Int3(p.x & rhs, p.y & rhs, p.z & rhs);
// bitwise disjunction (OR)
static public Int3 operator |(Int3 p1, Int3 p2) => new Int3(p1.x | p2.x, p1.y | p2.y, p1.z | p2.z);
static public Int3 operator |(Int3 p, int rhs) => new Int3(p.x | rhs, p.y | rhs, p.z | rhs);
// bitwise exclusive disjunction (XOR)
static public Int3 operator ^(Int3 p1, Int3 p2) => new Int3(p1.x ^ p2.x, p1.y ^ p2.y, p1.z ^ p2.z);
static public Int3 operator ^(Int3 p, int rhs) => new Int3(p.x ^ rhs, p.y ^ rhs, p.z ^ rhs);
// Vector3 conversion
static public implicit operator Vector3(Int3 p) => new Vector3(p.x, p.y, p.z);
static public explicit operator Int3(Vector3 p) => new Int3((int)p.x, (int)p.y, (int)p.z);
public Vector3 ToVector3() => (Vector3)this;
// Vector3Int conversion
static public implicit operator Vector3Int(Int3 p) => new Vector3Int(p.x, p.y, p.z);
static public implicit operator Int3(Vector3Int p) => new Int3(p.x, p.y, p.z);
public Vector3Int ToVector3Int() => (Vector3Int)this;
// Equality
public bool Equals(Int3 other)
=> (other.x == x && other.y == y && other.z == z);
public bool Equals(Vector3Int other)
=> (other.x == x && other.y == y && other.z == z);
public override bool Equals(object obj) {
if(obj is null || !(obj is Int3)) return false;
return Equals((Int3)obj);
}
static public bool operator ==(Int3 p1, Int3 p2) => p1.Equals(p2);
static public bool operator !=(Int3 p1, Int3 p2) => !p1.Equals(p2);
static public bool operator ==(Int3 p, Vector3Int vec) => p.Equals(vec);
static public bool operator !=(Int3 p, Vector3Int vec) => !p.Equals(vec);
static public bool operator ==(Vector3Int vec, Int3 p) => p.Equals(vec);
static public bool operator !=(Vector3Int vec, Int3 p) => !p.Equals(vec);
// Hashcode
public override int GetHashCode() { // sdbm
int hash = -1;
unchecked {
hash = x + (hash << 6) + (hash << 16) - hash;
hash = y + (hash << 6) + (hash << 16) - hash;
hash = z + (hash << 6) + (hash << 16) - hash;
}
return hash;
}
// ToString
public override string ToString() => $"({x}, {y}, {z})";
}
static public class Int3Ex {
/// <summary>
/// Static. Computes a 'flattened' array index for specified coordinates within volume <c>size</c>. Cheap.
/// <para>(Also supports area index computation when <c>z</c> equals 0.)</para>
/// </summary>
/// <param name="size">All components must be greater than 0 (except <c>z</c> for the special case of computing the area index).</param>
#if DEBUG || UNITY_EDITOR
static public int Int3ToFlat(Int3 p, Int3 size) {
if(size.x <= 0 || size.y <= 0 || size.z < 0) throw new ArgumentOutOfRangeException(nameof(size), $"Invalid size {size}.");
if(p.x >= size.x || p.y >= size.y || p.z >= size.z) throw new ArgumentOutOfRangeException(nameof(p), $"Cursor {p} exceeds {size-Int3.one}.");
return p.z * (size.x * size.y) + p.y * size.x + p.x;
}
#else // no error checking
static public int Int3ToFlat(Int3 p, Int3 size)
=> p.z * (size.x * size.y) + p.y * size.x + p.x;
#endif
/// <summary>
/// Extension. Computes a 'flattened' array index for current coordinates within volume <c>size</c>.
/// <para>(Also supports area index computation when <c>z</c> equals 0.)</para>
/// </summary>
/// <param name="box">All components must be greater than 0 (except <c>z</c> for the special case of computing the area index).</param>
static public int ToFlatIndex(this Int3 p, Int3 size) => Int3ToFlat(p, size);
/// <summary>
/// Static. Computes x,y,z coordinates for a specified 'flat' array index within volume <c>size</c>. Expensive.
/// <para>(Also supports area index computation when <c>z</c> equals 0.)</para>
/// </summary>
/// <param name="index">Index.</param>
/// <param name="box">Size components must be greater than 0 (except <c>z</c> for the special case of computing the area index).</param>
#if DEBUG || UNITY_EDITOR
static public Int3 FlatToInt3(int index, Int3 size) {
if(size.z > 0) {
if(size.x <= 0 || size.y <= 0) throw new ArgumentOutOfRangeException(nameof(size), $"Invalid width {size.x} or height {size.y}.");
if(index < 0 || index >= size.Mul()) throw new ArgumentOutOfRangeException(nameof(index), $"Index {index} exceeds {size-Int3.one}.");
return new Int3(index % size.z, (index / size.z) % size.y, index / (size.y * size.z));
}
if(size.x < 0) throw new ArgumentOutOfRangeException(nameof(size), $"Invalid width {size.x}.");
if(index < 0 || index >= size.x * size.y) throw new ArgumentOutOfRangeException(nameof(index), $"Index {index} exceeds {size-Int3.one}.");
return new Int3(index % size.x, index / size.x, 0);
}
#else // no error checking
static public Int3 FlatToInt3(int index, Int3 size)
=> (size.z > 0)? new Int3(index % size.z, (index / size.z) % size.y, index / (size.y * size.z));
: new Int3(index % size.x, index / size.x, 0);
#endif
/// <summary>
/// Static. Computes the length of the shortest distance along orthogonal paths (aka taxicab distance).
/// </summary>
static public int ManhattanDistance(Int3 p1, Int3 p2)
=> Int3.Distance(p1, p2).Sum();
/// <summary>
/// Static. Computes the length of the shortest Euclidean distance between two points.
/// </summary>
static public float EuclideanDistance(Int3 p1, Int3 p2)
=> (float)Math.Sqrt((p2 - p1).SqrSum());
/// <summary>
/// Static. Computes the length of the shortest distance by considering only orthogonal and/or diagonal movements (aka chess distance).
/// </summary>
static public float DiagonalDistance(Int3 p1, Int3 p2) {
const float SQR_2 = 1.414213562f;
const float SQR_3 = 1.732050807f;
Int3 d = Int3.Distance(p1, p2);
if(d.x == 0) {
if(d.y > d.z) return SQR_2 * d.z + (d.z - d.y);
return SQR_2 * d.y + (d.y - d.z);
} else if(d.y == 0) {
if(d.x > d.z) return SQR_2 * d.z + (d.z - d.x);
return SQR_2 * d.x + (d.x - d.z);
} else if(d.z == 0) {
if(d.x > d.y) return SQR_2 * d.y + (d.y - d.x);
return SQR_2 * d.x + (d.x - d.y);
}
int dmin = d[d.IndexOfMin()];
return SQR_3 * (float)dmin + DiagonalDistance(p1, p2 - Int3.Cube(dmin));
}
/// <summary>
/// Extension. Returns a new <c>EnumerableVolume</c>.
/// </summary>
static public EnumerableVolume ToEnumerable(this Int3 p, Permutation3D sequence = Permutation3D.XYZ, Int3 step = default)
=> new EnumerableVolume(p, sequence, step);
/// <summary>
/// Extension. Returns a new <c>Int3</c> whose components are permuted as specified.
/// </summary>
static public Int3 Permuted(this Int3 point, Permutation3D permutation = Permutation3D.XYZ) {
if(permutation == Permutation3D.XYZ) return point;
return Int3.ForEach( (i) => point[permutation.GetAxis(i)] );
}
/// <summary>
/// Extension. Returns a new <c>Int3</c> with selected axes set to zero and optionally permuted.
/// </summary>
static public Int3 Zeroed(this Int3 point, Axes3D axes, Permutation3D permutation = Permutation3D.XYZ) {
if(axes.HasFlag(Axes3D.X)) point[0] = 0;
if(axes.HasFlag(Axes3D.Y)) point[1] = 0;
if(axes.HasFlag(Axes3D.Z)) point[2] = 0;
return Permuted(point, permutation);
}
/// <summary>
/// Extension. Returns 0, 1, 2 if a zero value is stored in the x, y, z component, in that respective order. If there are no zeroes, returns -1.
/// </summary>
static public int FindZero(this Int3 p)
{ if(p.x == 0) return 0; if(p.y == 0) return 1; if(p.z == 0) return 2; return -1; }
/// <summary>
/// Extension. Returns a new <c>Int3</c> with x component replaced with set <c>value</c>.
/// </summary>
static public Int3 WithX(this Int3 p, int value) => new Int3(value, p.y, p.z);
/// <summary>
/// Extension. Returns a new <c>Int3</c> with y component replaced with set <c>value</c>.
/// </summary>
static public Int3 WithY(this Int3 p, int value) => new Int3(p.x, value, p.z);
/// <summary>
/// Extension. Returns a new <c>Int3</c> with z component replaced with set <c>value</c>.
/// </summary>
static public Int3 WithZ(this Int3 p, int value) => new Int3(p.x, p.y, value);
}
// swizzling shortcuts
public partial struct Int3 : IEquatable<Int3> {
public Int3 xyz => this;
public Int3 xzy => new Int3(x,z,y);
public Int3 yxz => new Int3(y,x,z);
public Int3 yzx => new Int3(y,z,x);
public Int3 zxy => new Int3(z,x,y);
public Int3 zyx => new Int3(z,y,x);
public Int3 xxx => new Int3(x,x,x);
public Int3 xxy => new Int3(x,x,y);
public Int3 xyx => new Int3(x,y,x);
public Int3 yxx => new Int3(y,x,x);
public Int3 yyy => new Int3(y,y,y);
public Int3 yyz => new Int3(y,y,z);
public Int3 yzy => new Int3(y,z,y);
public Int3 zyy => new Int3(z,y,y);
public Int3 yzz => new Int3(y,z,z);
public Int3 zzy => new Int3(z,z,y);
public Int3 zyz => new Int3(z,y,z);
public Int3 zzz => new Int3(z,z,z);
public Int3 xxz => new Int3(x,x,z);
public Int3 xzx => new Int3(x,z,x);
public Int3 zxx => new Int3(z,x,x);
public Int3 zzx => new Int3(z,z,x);
public Int3 zxz => new Int3(z,x,z);
public Int3 xzz => new Int3(x,z,z);
public Int3 yyx => new Int3(y,y,x);
public Int3 yxy => new Int3(y,x,y);
public Int3 xyy => new Int3(x,y,y);
public Int3 _yz => new Int3(0,y,z);
public Int3 _zy => new Int3(0,z,y);
public Int3 _xz => new Int3(0,x,z);
public Int3 _zx => new Int3(0,z,x);
public Int3 _xy => new Int3(0,x,y);
public Int3 _yx => new Int3(0,y,x);
public Int3 x_z => new Int3(x,0,z);
public Int3 x_y => new Int3(x,0,y);
public Int3 y_z => new Int3(y,0,z);
public Int3 y_x => new Int3(y,0,x);
public Int3 z_y => new Int3(z,0,y);
public Int3 z_x => new Int3(z,0,x);
public Int3 xy_ => new Int3(x,y,0);
public Int3 xz_ => new Int3(x,z,0);
public Int3 yx_ => new Int3(y,x,0);
public Int3 yz_ => new Int3(y,z,0);
public Int3 zx_ => new Int3(z,x,0);
public Int3 zy_ => new Int3(z,y,0);
public Int3 __x => new Int3(0,0,x);
public Int3 __y => new Int3(0,0,y);
public Int3 __z => new Int3(0,0,z);
public Int3 _x_ => new Int3(0,x,0);
public Int3 _y_ => new Int3(0,y,0);
public Int3 _z_ => new Int3(0,z,0);
public Int3 x__ => new Int3(x,0,0);
public Int3 y__ => new Int3(y,0,0);
public Int3 z__ => new Int3(z,0,0);
}
}
(BoxInt is just another type that is an immutable discrete Rect-like, but in 3D. Unlike BoundsInt it natively supports discrete volume enumeration among other things, useful for voxels and such. Not included here.)
edit: typo
I used Vector2Int and Vector3Int several times in different scenarios. Yes, sometimes just a quick way to store 2-3 values. Now, I wanted to store 4 floats and Vector4 worked fine. Now I just wanted to write same version for Vector4Int and… it was not there. I am pretty angry that I must stop coding and finding this thread and reading mutliple great argumented answers how I totally do not need it. Omg… let me judge by myself please. I want it and need it. Maybe not for most dedicated things ever, but it would be just great as it works great witn Vector3Int and Vector2Int… OK, I will add one more useles struct as I do not have better option for my great RPG to be released soon anyways. Vector4Int would be just nice and unified. This engine is called Unity at the end. agrhhhh rrrrrrrrrr
upd: One argument more for having Vector4Int is pretty drawing in inspector as one line, sometimes it makes sense for connected lesser values
Vector4 doesn’t draw as a pretty one line, it does the default drawing that any plane old serializable class/struct draws as:
If you need a container for 4 int, it’s trivial:
public class zTest01 : MonoBehaviour
{
public Vector2 vec2;
public Vector3 vec3;
public Vector4 vec4;
public Vector2Int vec2int;
public Vector3Int vec3int;
public Vector4Int vec4IntCustom;
[System.Serializable]
public struct Vector4Int
{
public int x, y, z, w;
}
}
You wouldn’t have to stop coding and come looking here.
I get it, y’all want the thing. But unfortunately it doesn’t exist. Thing is it has been demonstrated how to get it in a minimal amount of code. Now you got it! Problem solved.