Added fixedpoint 4 (#29834)
* Added fixedpoint 4, which is basically just fixedpoint2 but with 4 points of precision and using a long instead of an int to store values.
This commit is contained in:
@@ -48,6 +48,8 @@ namespace Content.Shared.FixedPoint
|
|||||||
|
|
||||||
public static FixedPoint2 FromCents(int value) => new(value);
|
public static FixedPoint2 FromCents(int value) => new(value);
|
||||||
|
|
||||||
|
public static FixedPoint2 FromHundredths(int value) => new(value);
|
||||||
|
|
||||||
public static FixedPoint2 New(float value)
|
public static FixedPoint2 New(float value)
|
||||||
{
|
{
|
||||||
return new((int) ApplyFloatEpsilon(value * ShiftConstant));
|
return new((int) ApplyFloatEpsilon(value * ShiftConstant));
|
||||||
@@ -245,14 +247,14 @@ namespace Content.Shared.FixedPoint
|
|||||||
return FixedPoint2.Abs(a - b);
|
return FixedPoint2.Abs(a - b);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FixedPoint2 Clamp(FixedPoint2 reagent, FixedPoint2 min, FixedPoint2 max)
|
public static FixedPoint2 Clamp(FixedPoint2 number, FixedPoint2 min, FixedPoint2 max)
|
||||||
{
|
{
|
||||||
if (min > max)
|
if (min > max)
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"{nameof(min)} {min} cannot be larger than {nameof(max)} {max}");
|
throw new ArgumentException($"{nameof(min)} {min} cannot be larger than {nameof(max)} {max}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return reagent < min ? min : reagent > max ? max : reagent;
|
return number < min ? min : number > max ? max : number;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override readonly bool Equals(object? obj)
|
public override readonly bool Equals(object? obj)
|
||||||
@@ -274,7 +276,7 @@ namespace Content.Shared.FixedPoint
|
|||||||
if (value == "MaxValue")
|
if (value == "MaxValue")
|
||||||
Value = int.MaxValue;
|
Value = int.MaxValue;
|
||||||
else
|
else
|
||||||
this = New(Parse.Float(value));
|
this = New(Parse.Double(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override readonly string ToString() => $"{ShiftDown().ToString(CultureInfo.InvariantCulture)}";
|
public override readonly string ToString() => $"{ShiftDown().ToString(CultureInfo.InvariantCulture)}";
|
||||||
@@ -314,7 +316,7 @@ namespace Content.Shared.FixedPoint
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class FixedPointEnumerableExt
|
public static class FixedPoint2EnumerableExt
|
||||||
{
|
{
|
||||||
public static FixedPoint2 Sum(this IEnumerable<FixedPoint2> source)
|
public static FixedPoint2 Sum(this IEnumerable<FixedPoint2> source)
|
||||||
{
|
{
|
||||||
|
|||||||
339
Content.Shared/FixedPoint/FixedPoint4.cs
Normal file
339
Content.Shared/FixedPoint/FixedPoint4.cs
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Shared.FixedPoint
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a quantity of something, to a precision of 0.01.
|
||||||
|
/// To enforce this level of precision, floats are shifted by 2 decimal points, rounded, and converted to an int.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, CopyByRef]
|
||||||
|
public struct FixedPoint4 : ISelfSerialize, IComparable<FixedPoint4>, IEquatable<FixedPoint4>, IFormattable
|
||||||
|
{
|
||||||
|
public long Value { get; private set; }
|
||||||
|
private const long Shift = 4;
|
||||||
|
private const long ShiftConstant = 10000; // Must be equal to pow(10, Shift)
|
||||||
|
|
||||||
|
public static FixedPoint4 MaxValue { get; } = new(long.MaxValue);
|
||||||
|
public static FixedPoint4 Epsilon { get; } = new(1);
|
||||||
|
public static FixedPoint4 Zero { get; } = new(0);
|
||||||
|
|
||||||
|
// This value isn't picked by any proper testing, don't @ me.
|
||||||
|
private const float FloatEpsilon = 0.00001f;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
static FixedPoint4()
|
||||||
|
{
|
||||||
|
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
||||||
|
DebugTools.Assert(Math.Pow(10, Shift) == ShiftConstant, "ShiftConstant must be equal to pow(10, Shift)");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private readonly double ShiftDown()
|
||||||
|
{
|
||||||
|
return Value / (double) ShiftConstant;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FixedPoint4(long value)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FixedPoint4 New(long value)
|
||||||
|
{
|
||||||
|
return new(value * ShiftConstant);
|
||||||
|
}
|
||||||
|
public static FixedPoint4 FromTenThousandths(long value) => new(value);
|
||||||
|
|
||||||
|
public static FixedPoint4 New(float value)
|
||||||
|
{
|
||||||
|
return new((long) ApplyFloatEpsilon(value * ShiftConstant));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float ApplyFloatEpsilon(float value)
|
||||||
|
{
|
||||||
|
return value + FloatEpsilon * Math.Sign(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double ApplyFloatEpsilon(double value)
|
||||||
|
{
|
||||||
|
return value + FloatEpsilon * Math.Sign(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create the closest <see cref="FixedPoint4"/> for a float value, always rounding up.
|
||||||
|
/// </summary>
|
||||||
|
public static FixedPoint4 NewCeiling(float value)
|
||||||
|
{
|
||||||
|
return new((long) MathF.Ceiling(value * ShiftConstant));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FixedPoint4 New(double value)
|
||||||
|
{
|
||||||
|
return new((long) ApplyFloatEpsilon(value * ShiftConstant));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FixedPoint4 New(string value)
|
||||||
|
{
|
||||||
|
return New(Parse.Float(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FixedPoint4 operator +(FixedPoint4 a) => a;
|
||||||
|
|
||||||
|
public static FixedPoint4 operator -(FixedPoint4 a) => new(-a.Value);
|
||||||
|
|
||||||
|
public static FixedPoint4 operator +(FixedPoint4 a, FixedPoint4 b)
|
||||||
|
=> new(a.Value + b.Value);
|
||||||
|
|
||||||
|
public static FixedPoint4 operator -(FixedPoint4 a, FixedPoint4 b)
|
||||||
|
=> new(a.Value - b.Value);
|
||||||
|
|
||||||
|
public static FixedPoint4 operator *(FixedPoint4 a, FixedPoint4 b)
|
||||||
|
{
|
||||||
|
return new(b.Value * a.Value / ShiftConstant);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FixedPoint4 operator *(FixedPoint4 a, float b)
|
||||||
|
{
|
||||||
|
return new((long) ApplyFloatEpsilon(a.Value * b));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FixedPoint4 operator *(FixedPoint4 a, double b)
|
||||||
|
{
|
||||||
|
return new((long) ApplyFloatEpsilon(a.Value * b));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FixedPoint4 operator *(FixedPoint4 a, long b)
|
||||||
|
{
|
||||||
|
return new(a.Value * b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FixedPoint4 operator /(FixedPoint4 a, FixedPoint4 b)
|
||||||
|
{
|
||||||
|
return new((long) (ShiftConstant * (long) a.Value / b.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FixedPoint4 operator /(FixedPoint4 a, float b)
|
||||||
|
{
|
||||||
|
return new((long) ApplyFloatEpsilon(a.Value / b));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator <=(FixedPoint4 a, long b)
|
||||||
|
{
|
||||||
|
return a <= New(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator >=(FixedPoint4 a, long b)
|
||||||
|
{
|
||||||
|
return a >= New(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator <(FixedPoint4 a, long b)
|
||||||
|
{
|
||||||
|
return a < New(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator >(FixedPoint4 a, long b)
|
||||||
|
{
|
||||||
|
return a > New(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(FixedPoint4 a, long b)
|
||||||
|
{
|
||||||
|
return a == New(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(FixedPoint4 a, long b)
|
||||||
|
{
|
||||||
|
return a != New(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(FixedPoint4 a, FixedPoint4 b)
|
||||||
|
{
|
||||||
|
return a.Equals(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(FixedPoint4 a, FixedPoint4 b)
|
||||||
|
{
|
||||||
|
return !a.Equals(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator <=(FixedPoint4 a, FixedPoint4 b)
|
||||||
|
{
|
||||||
|
return a.Value <= b.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator >=(FixedPoint4 a, FixedPoint4 b)
|
||||||
|
{
|
||||||
|
return a.Value >= b.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator <(FixedPoint4 a, FixedPoint4 b)
|
||||||
|
{
|
||||||
|
return a.Value < b.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator >(FixedPoint4 a, FixedPoint4 b)
|
||||||
|
{
|
||||||
|
return a.Value > b.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly float Float()
|
||||||
|
{
|
||||||
|
return (float) ShiftDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly double Double()
|
||||||
|
{
|
||||||
|
return ShiftDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly long Long()
|
||||||
|
{
|
||||||
|
return Value / ShiftConstant;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly int Int()
|
||||||
|
{
|
||||||
|
return (int)Long();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implicit operators ftw
|
||||||
|
public static implicit operator FixedPoint4(FixedPoint2 n) => New(n.Int());
|
||||||
|
public static implicit operator FixedPoint4(float n) => New(n);
|
||||||
|
public static implicit operator FixedPoint4(double n) => New(n);
|
||||||
|
public static implicit operator FixedPoint4(int n) => New(n);
|
||||||
|
public static implicit operator FixedPoint4(long n) => New(n);
|
||||||
|
|
||||||
|
public static explicit operator FixedPoint2(FixedPoint4 n) => n.Int();
|
||||||
|
public static explicit operator float(FixedPoint4 n) => n.Float();
|
||||||
|
public static explicit operator double(FixedPoint4 n) => n.Double();
|
||||||
|
public static explicit operator int(FixedPoint4 n) => n.Int();
|
||||||
|
public static explicit operator long(FixedPoint4 n) => n.Long();
|
||||||
|
|
||||||
|
public static FixedPoint4 Min(params FixedPoint4[] fixedPoints)
|
||||||
|
{
|
||||||
|
return fixedPoints.Min();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FixedPoint4 Min(FixedPoint4 a, FixedPoint4 b)
|
||||||
|
{
|
||||||
|
return a < b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FixedPoint4 Max(FixedPoint4 a, FixedPoint4 b)
|
||||||
|
{
|
||||||
|
return a > b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long Sign(FixedPoint4 value)
|
||||||
|
{
|
||||||
|
if (value < Zero)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value > Zero)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FixedPoint4 Abs(FixedPoint4 a)
|
||||||
|
{
|
||||||
|
return FromTenThousandths(Math.Abs(a.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FixedPoint4 Dist(FixedPoint4 a, FixedPoint4 b)
|
||||||
|
{
|
||||||
|
return FixedPoint4.Abs(a - b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FixedPoint4 Clamp(FixedPoint4 number, FixedPoint4 min, FixedPoint4 max)
|
||||||
|
{
|
||||||
|
if (min > max)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"{nameof(min)} {min} cannot be larger than {nameof(max)} {max}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return number < min ? min : number > max ? max : number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override readonly bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
return obj is FixedPoint4 unit &&
|
||||||
|
Value == unit.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override readonly int GetHashCode()
|
||||||
|
{
|
||||||
|
// ReSharper disable once NonReadonlyMemberInGetHashCode
|
||||||
|
return HashCode.Combine(Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Deserialize(string value)
|
||||||
|
{
|
||||||
|
// TODO implement "lossless" serializer.
|
||||||
|
// I.e., dont use floats.
|
||||||
|
if (value == "MaxValue")
|
||||||
|
Value = int.MaxValue;
|
||||||
|
else
|
||||||
|
this = New(Parse.Double(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override readonly string ToString() => $"{ShiftDown().ToString(CultureInfo.InvariantCulture)}";
|
||||||
|
|
||||||
|
public string ToString(string? format, IFormatProvider? formatProvider)
|
||||||
|
{
|
||||||
|
return ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly string Serialize()
|
||||||
|
{
|
||||||
|
// TODO implement "lossless" serializer.
|
||||||
|
// I.e., dont use floats.
|
||||||
|
if (Value == int.MaxValue)
|
||||||
|
return "MaxValue";
|
||||||
|
|
||||||
|
return ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly bool Equals(FixedPoint4 other)
|
||||||
|
{
|
||||||
|
return Value == other.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly int CompareTo(FixedPoint4 other)
|
||||||
|
{
|
||||||
|
if (other.Value > Value)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (other.Value < Value)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FixedPoint4EnumerableExt
|
||||||
|
{
|
||||||
|
public static FixedPoint4 Sum(this IEnumerable<FixedPoint4> source)
|
||||||
|
{
|
||||||
|
var acc = FixedPoint4.Zero;
|
||||||
|
|
||||||
|
foreach (var n in source)
|
||||||
|
{
|
||||||
|
acc += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user