]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Added fixedpoint 4 (#29834)
authorJezithyr <jezithyr@gmail.com>
Mon, 22 Jul 2024 21:07:31 +0000 (14:07 -0700)
committerGitHub <noreply@github.com>
Mon, 22 Jul 2024 21:07:31 +0000 (14:07 -0700)
* 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.

Content.Shared/FixedPoint/FixedPoint2.cs
Content.Shared/FixedPoint/FixedPoint4.cs [new file with mode: 0644]

index 6439ee6c5e2d9c2b4dc7b222c59b7fe904652517..7316d22f113a7a27f4c3c2d5e729dd58300f1d9b 100644 (file)
@@ -48,6 +48,8 @@ namespace Content.Shared.FixedPoint
 
         public static FixedPoint2 FromCents(int value) => new(value);
 
+        public static FixedPoint2 FromHundredths(int value) => new(value);
+
         public static FixedPoint2 New(float value)
         {
             return new((int) ApplyFloatEpsilon(value * ShiftConstant));
@@ -245,14 +247,14 @@ namespace Content.Shared.FixedPoint
             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)
             {
                 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)
@@ -274,7 +276,7 @@ namespace Content.Shared.FixedPoint
             if (value == "MaxValue")
                 Value = int.MaxValue;
             else
-                this = New(Parse.Float(value));
+                this = New(Parse.Double(value));
         }
 
         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)
         {
diff --git a/Content.Shared/FixedPoint/FixedPoint4.cs b/Content.Shared/FixedPoint/FixedPoint4.cs
new file mode 100644 (file)
index 0000000..b17587f
--- /dev/null
@@ -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;
+        }
+    }
+}