forked from space-wizards/space-station-14
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added fixedpoint 4 (space-wizards#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.
- Loading branch information
Showing
2 changed files
with
345 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} | ||
} |