mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 02:50:32 +00:00
feat(Compression): Vector4Long for Quaternion Delta compression (V1) (#3907)
Co-authored-by: mischa <info@noobtuts.com>
This commit is contained in:
parent
b0e60a8e3d
commit
2277d66cea
@ -71,6 +71,24 @@ public static bool ScaleToLong(Vector3 value, float precision, out long x, out l
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns
|
||||||
|
// 'true' if scaling was possible within 'long' bounds.
|
||||||
|
// 'false' if clamping was necessary.
|
||||||
|
// never throws. checking result is optional.
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool ScaleToLong(Quaternion value, float precision, out long x, out long y, out long z, out long w)
|
||||||
|
{
|
||||||
|
// attempt to convert every component.
|
||||||
|
// do not return early if one conversion returned 'false'.
|
||||||
|
// the return value is optional. always attempt to convert all.
|
||||||
|
bool result = true;
|
||||||
|
result &= ScaleToLong(value.x, precision, out x);
|
||||||
|
result &= ScaleToLong(value.y, precision, out y);
|
||||||
|
result &= ScaleToLong(value.z, precision, out z);
|
||||||
|
result &= ScaleToLong(value.w, precision, out w);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool ScaleToLong(Vector3 value, float precision, out Vector3Long quantized)
|
public static bool ScaleToLong(Vector3 value, float precision, out Vector3Long quantized)
|
||||||
{
|
{
|
||||||
@ -78,6 +96,13 @@ public static bool ScaleToLong(Vector3 value, float precision, out Vector3Long q
|
|||||||
return ScaleToLong(value, precision, out quantized.x, out quantized.y, out quantized.z);
|
return ScaleToLong(value, precision, out quantized.x, out quantized.y, out quantized.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool ScaleToLong(Quaternion value, float precision, out Vector4Long quantized)
|
||||||
|
{
|
||||||
|
quantized = Vector4Long.zero;
|
||||||
|
return ScaleToLong(value, precision, out quantized.x, out quantized.y, out quantized.z, out quantized.w);
|
||||||
|
}
|
||||||
|
|
||||||
// multiple by precision.
|
// multiple by precision.
|
||||||
// for example, 0.1 cm precision converts '50' long to '5.0f' float.
|
// for example, 0.1 cm precision converts '50' long to '5.0f' float.
|
||||||
public static float ScaleToFloat(long value, float precision)
|
public static float ScaleToFloat(long value, float precision)
|
||||||
@ -103,10 +128,25 @@ public static Vector3 ScaleToFloat(long x, long y, long z, float precision)
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Quaternion ScaleToFloat(long x, long y, long z, long w, float precision)
|
||||||
|
{
|
||||||
|
Quaternion v;
|
||||||
|
v.x = ScaleToFloat(x, precision);
|
||||||
|
v.y = ScaleToFloat(y, precision);
|
||||||
|
v.z = ScaleToFloat(z, precision);
|
||||||
|
v.w = ScaleToFloat(w, precision);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static Vector3 ScaleToFloat(Vector3Long value, float precision) =>
|
public static Vector3 ScaleToFloat(Vector3Long value, float precision) =>
|
||||||
ScaleToFloat(value.x, value.y, value.z, precision);
|
ScaleToFloat(value.x, value.y, value.z, precision);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Quaternion ScaleToFloat(Vector4Long value, float precision) =>
|
||||||
|
ScaleToFloat(value.x, value.y, value.z, value.w, precision);
|
||||||
|
|
||||||
// scale a float within min/max range to an ushort between min/max range
|
// scale a float within min/max range to an ushort between min/max range
|
||||||
// note: can also use this for byte range from byte.MinValue to byte.MaxValue
|
// note: can also use this for byte range from byte.MinValue to byte.MaxValue
|
||||||
public static ushort ScaleFloatToUShort(float value, float minValue, float maxValue, ushort minTarget, ushort maxTarget)
|
public static ushort ScaleFloatToUShort(float value, float minValue, float maxValue, ushort minTarget, ushort maxTarget)
|
||||||
|
@ -26,6 +26,16 @@ public static void Compress(NetworkWriter writer, Vector3Long last, Vector3Long
|
|||||||
Compress(writer, last.z, current.z);
|
Compress(writer, last.z, current.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delta (usually small), then zigzag varint to support +- changes
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void Compress(NetworkWriter writer, Vector4Long last, Vector4Long current)
|
||||||
|
{
|
||||||
|
Compress(writer, last.x, current.x);
|
||||||
|
Compress(writer, last.y, current.y);
|
||||||
|
Compress(writer, last.z, current.z);
|
||||||
|
Compress(writer, last.w, current.w);
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static Vector3Long Decompress(NetworkReader reader, Vector3Long last)
|
public static Vector3Long Decompress(NetworkReader reader, Vector3Long last)
|
||||||
{
|
{
|
||||||
@ -34,5 +44,15 @@ public static Vector3Long Decompress(NetworkReader reader, Vector3Long last)
|
|||||||
long z = Decompress(reader, last.z);
|
long z = Decompress(reader, last.z);
|
||||||
return new Vector3Long(x, y, z);
|
return new Vector3Long(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Vector4Long Decompress(NetworkReader reader, Vector4Long last)
|
||||||
|
{
|
||||||
|
long x = Decompress(reader, last.x);
|
||||||
|
long y = Decompress(reader, last.y);
|
||||||
|
long z = Decompress(reader, last.z);
|
||||||
|
long w = Decompress(reader, last.w);
|
||||||
|
return new Vector4Long(x, y, z, w);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
126
Assets/Mirror/Core/Tools/Vector4Long.cs
Normal file
126
Assets/Mirror/Core/Tools/Vector4Long.cs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
#pragma warning disable CS0659 // 'Vector4Long' overrides Object.Equals(object o) but does not override Object.GetHashCode()
|
||||||
|
#pragma warning disable CS0661 // 'Vector4Long' defines operator == or operator != but does not override Object.GetHashCode()
|
||||||
|
|
||||||
|
// Vector4Long by mischa (based on game engine project)
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Mirror
|
||||||
|
{
|
||||||
|
public struct Vector4Long
|
||||||
|
{
|
||||||
|
public long x;
|
||||||
|
public long y;
|
||||||
|
public long z;
|
||||||
|
public long w;
|
||||||
|
|
||||||
|
public static readonly Vector4Long zero = new Vector4Long(0, 0, 0, 0);
|
||||||
|
public static readonly Vector4Long one = new Vector4Long(1, 1, 1, 1);
|
||||||
|
|
||||||
|
// constructor /////////////////////////////////////////////////////////
|
||||||
|
public Vector4Long(long x, long y, long z, long w)
|
||||||
|
{
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
this.w = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// operators ///////////////////////////////////////////////////////////
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Vector4Long operator +(Vector4Long a, Vector4Long b) =>
|
||||||
|
new Vector4Long(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Vector4Long operator -(Vector4Long a, Vector4Long b) =>
|
||||||
|
new Vector4Long(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Vector4Long operator -(Vector4Long v) =>
|
||||||
|
new Vector4Long(-v.x, -v.y, -v.z, -v.w);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Vector4Long operator *(Vector4Long a, long n) =>
|
||||||
|
new Vector4Long(a.x * n, a.y * n, a.z * n, a.w * n);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Vector4Long operator *(long n, Vector4Long a) =>
|
||||||
|
new Vector4Long(a.x * n, a.y * n, a.z * n, a.w * n);
|
||||||
|
|
||||||
|
// == returns true if approximately equal (with epsilon).
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool operator ==(Vector4Long a, Vector4Long b) =>
|
||||||
|
a.x == b.x &&
|
||||||
|
a.y == b.y &&
|
||||||
|
a.z == b.z &&
|
||||||
|
a.w == b.w;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool operator !=(Vector4Long a, Vector4Long b) => !(a == b);
|
||||||
|
|
||||||
|
// NO IMPLICIT System.Numerics.Vector4Long conversion because double<->float
|
||||||
|
// would silently lose precision in large worlds.
|
||||||
|
|
||||||
|
// [i] component index. useful for iterating all components etc.
|
||||||
|
public long this[int index]
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
case 0: return x;
|
||||||
|
case 1: return y;
|
||||||
|
case 2: return z;
|
||||||
|
case 3: return w;
|
||||||
|
default: throw new IndexOutOfRangeException($"Vector4Long[{index}] out of range.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
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($"Vector4Long[{index}] out of range.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// instance functions //////////////////////////////////////////////////
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public override string ToString() => $"({x} {y} {z} {w})";
|
||||||
|
|
||||||
|
// equality ////////////////////////////////////////////////////////////
|
||||||
|
// implement Equals & HashCode explicitly for performance.
|
||||||
|
// calling .Equals (instead of "==") checks for exact equality.
|
||||||
|
// (API compatibility)
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool Equals(Vector4Long other) =>
|
||||||
|
x == other.x && y == other.y && z == other.z && w == other.w;
|
||||||
|
|
||||||
|
// Equals(object) can reuse Equals(Vector4)
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public override bool Equals(object other) =>
|
||||||
|
other is Vector4Long vector4 && Equals(vector4);
|
||||||
|
|
||||||
|
#if UNITY_2021_3_OR_NEWER
|
||||||
|
// Unity 2019/2020 don't have HashCode.Combine yet.
|
||||||
|
// this is only to avoid reflection. without defining, it works too.
|
||||||
|
// default generated by rider
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public override int GetHashCode() => HashCode.Combine(x, y, z, w);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
3
Assets/Mirror/Core/Tools/Vector4Long.cs.meta
Normal file
3
Assets/Mirror/Core/Tools/Vector4Long.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9f69bf6b6d73476ab3f31dd635bb6497
|
||||||
|
timeCreated: 1726777293
|
@ -86,6 +86,32 @@ public void ScaleToLong_Vector3_OutOfRange()
|
|||||||
Assert.That(z, Is.EqualTo(long.MinValue));
|
Assert.That(z, Is.EqualTo(long.MinValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ScaleToLong_Quaternion()
|
||||||
|
{
|
||||||
|
// 0, positive, negative
|
||||||
|
Assert.True(Compression.ScaleToLong(new Quaternion(0, 10.5f, -100.5f, -10.5f), 0.1f, out long x, out long y, out long z, out long w));
|
||||||
|
Assert.That(x, Is.EqualTo(0));
|
||||||
|
Assert.That(y, Is.EqualTo(105));
|
||||||
|
Assert.That(z, Is.EqualTo(-1005));
|
||||||
|
Assert.That(w, Is.EqualTo(-105));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ScaleToLong_Quaternion_OutOfRange()
|
||||||
|
{
|
||||||
|
float precision = 0.1f;
|
||||||
|
float largest = long.MaxValue / 0.1f;
|
||||||
|
float smallest = long.MinValue / 0.1f;
|
||||||
|
|
||||||
|
// 0, largest, smallest
|
||||||
|
Assert.False(Compression.ScaleToLong(new Quaternion(0, largest, smallest, 0), precision, out long x, out long y, out long z, out long w));
|
||||||
|
Assert.That(x, Is.EqualTo(0));
|
||||||
|
Assert.That(y, Is.EqualTo(long.MaxValue));
|
||||||
|
Assert.That(z, Is.EqualTo(long.MinValue));
|
||||||
|
Assert.That(w, Is.EqualTo(0));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void ScaleToFloat()
|
public void ScaleToFloat()
|
||||||
{
|
{
|
||||||
@ -130,6 +156,17 @@ public void ScaleToFloat_Vector3()
|
|||||||
Assert.That(v.z, Is.EqualTo(-100.5f));
|
Assert.That(v.z, Is.EqualTo(-100.5f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ScaleToFloat_Quaternion()
|
||||||
|
{
|
||||||
|
// 0, positive, negative
|
||||||
|
Quaternion q = Compression.ScaleToFloat(0, 105, -1005, -105, 0.1f);
|
||||||
|
Assert.That(q.x, Is.EqualTo(0));
|
||||||
|
Assert.That(q.y, Is.EqualTo(10.5f));
|
||||||
|
Assert.That(q.z, Is.EqualTo(-100.5f));
|
||||||
|
Assert.That(q.w, Is.EqualTo(-10.5f));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void LargestAbsoluteComponentIndex()
|
public void LargestAbsoluteComponentIndex()
|
||||||
{
|
{
|
||||||
|
@ -59,7 +59,7 @@ public void Compress_Long_Changed_Large()
|
|||||||
|
|
||||||
// two components = 8 bytes changed.
|
// two components = 8 bytes changed.
|
||||||
// delta should compress it down a lot.
|
// delta should compress it down a lot.
|
||||||
// 7000 delta should use a few more bytse.
|
// 7000 delta should use a few more bytes.
|
||||||
Assert.That(writer.Position, Is.EqualTo(3));
|
Assert.That(writer.Position, Is.EqualTo(3));
|
||||||
|
|
||||||
// decompress should get original result
|
// decompress should get original result
|
||||||
@ -123,7 +123,7 @@ public void Compress_Vector3Long_YZChanged_Large()
|
|||||||
|
|
||||||
// two components = 8 bytes changed.
|
// two components = 8 bytes changed.
|
||||||
// delta should compress it down a lot.
|
// delta should compress it down a lot.
|
||||||
// 7000 delta should use a few more bytse.
|
// 7000 delta should use a few more bytes.
|
||||||
Assert.That(writer.Position, Is.EqualTo(5));
|
Assert.That(writer.Position, Is.EqualTo(5));
|
||||||
|
|
||||||
// decompress should get original result
|
// decompress should get original result
|
||||||
@ -131,5 +131,69 @@ public void Compress_Vector3Long_YZChanged_Large()
|
|||||||
Vector3Long decompressed = DeltaCompression.Decompress(reader, last);
|
Vector3Long decompressed = DeltaCompression.Decompress(reader, last);
|
||||||
Assert.That(decompressed, Is.EqualTo(current));
|
Assert.That(decompressed, Is.EqualTo(current));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Compress_Vector4Long_Unchanged()
|
||||||
|
{
|
||||||
|
NetworkWriter writer = new NetworkWriter();
|
||||||
|
|
||||||
|
Vector4Long last = new Vector4Long(1, 2, 3, 4);
|
||||||
|
Vector4Long current = new Vector4Long(1, 2, 3, 4); // unchanged
|
||||||
|
|
||||||
|
// delta compress current against last
|
||||||
|
DeltaCompression.Compress(writer, last, current);
|
||||||
|
|
||||||
|
// nothing changed.
|
||||||
|
// delta should compress it down a lot.
|
||||||
|
Assert.That(writer.Position, Is.EqualTo(4));
|
||||||
|
|
||||||
|
// decompress should get original result
|
||||||
|
NetworkReader reader = new NetworkReader(writer);
|
||||||
|
Vector4Long decompressed = DeltaCompression.Decompress(reader, last);
|
||||||
|
Assert.That(decompressed, Is.EqualTo(current));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Compress_Vector4Long_ZWChanged()
|
||||||
|
{
|
||||||
|
NetworkWriter writer = new NetworkWriter();
|
||||||
|
|
||||||
|
Vector4Long last = new Vector4Long(1, 2, 3, 4);
|
||||||
|
Vector4Long current = new Vector4Long(1, 2, 7, 8); // only 2 components change
|
||||||
|
|
||||||
|
// delta compress current against last
|
||||||
|
DeltaCompression.Compress(writer, last, current);
|
||||||
|
|
||||||
|
// two components = 8 bytes changed.
|
||||||
|
// delta should compress it down a lot.
|
||||||
|
Assert.That(writer.Position, Is.EqualTo(4));
|
||||||
|
|
||||||
|
// decompress should get original result
|
||||||
|
NetworkReader reader = new NetworkReader(writer);
|
||||||
|
Vector4Long decompressed = DeltaCompression.Decompress(reader, last);
|
||||||
|
Assert.That(decompressed, Is.EqualTo(current));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Compress_Vector4Long_ZWChanged_Large()
|
||||||
|
{
|
||||||
|
NetworkWriter writer = new NetworkWriter();
|
||||||
|
|
||||||
|
Vector4Long last = new Vector4Long(1, 2, 3, 4);
|
||||||
|
Vector4Long current = new Vector4Long(1, 2, 5, 7000); // only 2 components change
|
||||||
|
|
||||||
|
// delta compress current against last
|
||||||
|
DeltaCompression.Compress(writer, last, current);
|
||||||
|
|
||||||
|
// two components = 8 bytes changed.
|
||||||
|
// delta should compress it down a lot.
|
||||||
|
// 7000 delta should use a few more bytes.
|
||||||
|
Assert.That(writer.Position, Is.EqualTo(6));
|
||||||
|
|
||||||
|
// decompress should get original result
|
||||||
|
NetworkReader reader = new NetworkReader(writer);
|
||||||
|
Vector4Long decompressed = DeltaCompression.Decompress(reader, last);
|
||||||
|
Assert.That(decompressed, Is.EqualTo(current));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
154
Assets/Mirror/Tests/Editor/Tools/Vector4LongTests.cs
Normal file
154
Assets/Mirror/Tests/Editor/Tools/Vector4LongTests.cs
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
using System;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace Mirror.Tests.Tools
|
||||||
|
{
|
||||||
|
public class Vector4LongTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void Constructor()
|
||||||
|
{
|
||||||
|
Vector4Long v = new Vector4Long();
|
||||||
|
Assert.That(v.x, Is.EqualTo(0));
|
||||||
|
Assert.That(v.y, Is.EqualTo(0));
|
||||||
|
Assert.That(v.z, Is.EqualTo(0));
|
||||||
|
Assert.That(v.w, Is.EqualTo(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ConstructorXYZ()
|
||||||
|
{
|
||||||
|
Vector4Long v = new Vector4Long(1, 2, 3, 4);
|
||||||
|
Assert.That(v.x, Is.EqualTo(1));
|
||||||
|
Assert.That(v.y, Is.EqualTo(2));
|
||||||
|
Assert.That(v.z, Is.EqualTo(3));
|
||||||
|
Assert.That(v.w, Is.EqualTo(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void OperatorAdd()
|
||||||
|
{
|
||||||
|
Vector4Long a = new Vector4Long(1, 2, 3, 4);
|
||||||
|
Vector4Long b = new Vector4Long(2, 3, 4, 5);
|
||||||
|
Assert.That(a + b, Is.EqualTo(new Vector4Long(3, 5, 7, 9)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void OperatorSubtract()
|
||||||
|
{
|
||||||
|
Vector4Long a = new Vector4Long(1, 2, 3, 4);
|
||||||
|
Vector4Long b = new Vector4Long(-2, -3, -4, -5);
|
||||||
|
Assert.That(a - b, Is.EqualTo(new Vector4Long(3, 5, 7, 9)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void OperatorInverse()
|
||||||
|
{
|
||||||
|
Vector4Long v = new Vector4Long(1, 2, 3, 4);
|
||||||
|
Assert.That(-v, Is.EqualTo(new Vector4Long(-1, -2, -3, -4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void OperatorMultiply()
|
||||||
|
{
|
||||||
|
Vector4Long a = new Vector4Long(1, 2, 3, 4);
|
||||||
|
// a * n, n * a are two different operators. test both.
|
||||||
|
Assert.That(a * 2, Is.EqualTo(new Vector4Long(2, 4, 6, 8)));
|
||||||
|
Assert.That(2 * a, Is.EqualTo(new Vector4Long(2, 4, 6, 8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_2021_3_OR_NEWER
|
||||||
|
[Test]
|
||||||
|
public void OperatorEquals()
|
||||||
|
{
|
||||||
|
// two vectors which are approximately the same
|
||||||
|
Vector4Long a = new Vector4Long(1, 2, 3, 4);
|
||||||
|
Vector4Long b = new Vector4Long(1, 2, 3, 4);
|
||||||
|
Assert.That(a == b, Is.True);
|
||||||
|
|
||||||
|
// two vectors which are definitely not the same
|
||||||
|
Assert.That(a == Vector4Long.one, Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void OperatorNotEquals()
|
||||||
|
{
|
||||||
|
// two vectors which are approximately the same
|
||||||
|
Vector4Long a = new Vector4Long(1, 2, 3, 4);
|
||||||
|
Vector4Long b = new Vector4Long(1, 2, 3, 4);
|
||||||
|
Assert.That(a != b, Is.False);
|
||||||
|
|
||||||
|
// two vectors which are definitely not the same
|
||||||
|
Assert.That(a != Vector4Long.one, Is.True);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void OperatorIndexer()
|
||||||
|
{
|
||||||
|
Vector4Long a = new Vector4Long(1, 2, 3, 4);
|
||||||
|
|
||||||
|
// get
|
||||||
|
Assert.That(a[0], Is.EqualTo(1));
|
||||||
|
Assert.That(a[1], Is.EqualTo(2));
|
||||||
|
Assert.That(a[2], Is.EqualTo(3));
|
||||||
|
Assert.That(a[3], Is.EqualTo(4));
|
||||||
|
Assert.Throws<IndexOutOfRangeException>(() =>
|
||||||
|
{
|
||||||
|
double _ = a[-1];
|
||||||
|
});
|
||||||
|
Assert.Throws<IndexOutOfRangeException>(() =>
|
||||||
|
{
|
||||||
|
double _ = a[4];
|
||||||
|
});
|
||||||
|
|
||||||
|
// set
|
||||||
|
a[0] = -1;
|
||||||
|
a[1] = -2;
|
||||||
|
a[2] = -3;
|
||||||
|
a[3] = -4;
|
||||||
|
Assert.Throws<IndexOutOfRangeException>(() => { a[-1] = 0; });
|
||||||
|
Assert.Throws<IndexOutOfRangeException>(() => { a[4] = 0; });
|
||||||
|
Assert.That(a, Is.EqualTo(new Vector4Long(-1, -2, -3, -4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ToStringTest()
|
||||||
|
{
|
||||||
|
// should be rounded to :F2
|
||||||
|
Vector4Long v = new Vector4Long(-10, 0, 42, 3);
|
||||||
|
Assert.That(v.ToString(), Is.EqualTo("(-10 0 42 3)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_2021_3_OR_NEWER
|
||||||
|
[Test]
|
||||||
|
public void EqualsVector4Long()
|
||||||
|
{
|
||||||
|
Assert.That(Vector4Long.one.Equals(Vector4Long.one), Is.True);
|
||||||
|
Assert.That(Vector4Long.one.Equals(Vector4Long.zero), Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void EqualsObject()
|
||||||
|
{
|
||||||
|
Assert.That(Vector4Long.one.Equals((object)42), Is.False);
|
||||||
|
Assert.That(Vector4Long.one.Equals((object)Vector4Long.one), Is.True);
|
||||||
|
Assert.That(Vector4Long.one.Equals((object)Vector4Long.zero), Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetHashCodeTest()
|
||||||
|
{
|
||||||
|
// shouldn't be 0
|
||||||
|
Assert.That(Vector4Long.zero.GetHashCode(), !Is.EqualTo(0));
|
||||||
|
Assert.That(Vector4Long.one.GetHashCode(), !Is.EqualTo(0));
|
||||||
|
|
||||||
|
// should be same for same vector
|
||||||
|
Assert.That(Vector4Long.zero.GetHashCode(), Is.EqualTo(Vector4Long.zero.GetHashCode()));
|
||||||
|
|
||||||
|
// should be different for different vectors
|
||||||
|
Assert.That(Vector4Long.zero.GetHashCode(), !Is.EqualTo(Vector4Long.one.GetHashCode()));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a966f5fd1b865460abc471e46bcee889
|
||||||
|
timeCreated: 1666897728
|
Loading…
Reference in New Issue
Block a user