Another day, another utility. This time: Ranged values. They were inspired from a Unite Europe 2016 talk on scriptable objects (and the Inpector code is copied nearly verbatim) by Richard Fine. The original had only a minimum and maximum float value with the fancy Inspector GUI, but I extended it with functionality nobody in their right mind would probably ever need. It started with a method to get a random value from within the interval and quickly escalated to two interpolation functions and tests to determine if an interval contains a given value or another interval, or if two intervals intersect. Finally I added operators to add, multiply or order intervals. And as if all that wasn’t already useless enough, I did the same thing again, with ints instead of floats and added a generic Range<T> class, that can create intervals from any orderable value-type, albeit a bit less powerfull, since adding and subtracting doesn’t necessarily have meaning outside of numbers. The inspector code also doesn’t work with the generic version.
Anyway, here is a example of how it looks in the Inspector, and a wall of code after the break.
The editor code can be found on my Bitbucket repository.
using System; using UnityEngine; using Random = UnityEngine.Random; public struct Range<T> : IEquatable<Range<T>>, IComparable<Range<T>> where T : struct, IEquatable<T>, IComparable<T> { public T min { get; set; } public T max { get; set; } public Range(T min, T max) : this() { if (min.CompareTo(max) <= 0) { this.min = min; this.max = max; } else { this.min = max; this.max = min; } } public bool valid { get { return min.CompareTo(max) <= 0; } } public Range<T> Validate() { return valid ? this : new Range<T> { min = max, max = min }; } public bool Contains(T value) { return min.CompareTo(value) <= 0 && max.CompareTo(value) >= 0; } public bool Contains(Range<T> range) { return Contains(range.min) && Contains(range.max); } public bool Intersects(Range<T> range) { return Contains(range.min) && max.CompareTo(range.max) < 0 || Contains(range.max) && min.CompareTo(range.min) > 0; } public Range<T> Apply(Func<T, T> function) { return new Range<T> {min = function(min), max = function(max)}; } public int CompareTo(Range<T> range) { if (min.CompareTo(range.min) < 0 && max.CompareTo(range.max) < 0) return -1; if (min.CompareTo(range.min) > 0 && max.CompareTo(range.max) > 0) return 1; return 0; } public override bool Equals(object obj) { if (obj is Range<T>) { Range<T> range = (Range<T>) obj; return min.Equals(range.min) && max.Equals(range.max); } return false; } public override int GetHashCode() { return min.GetHashCode() ^ max.GetHashCode(); } public bool Equals(Range<T> range) { return min.Equals(range.min) && max.Equals(range.max); } } [Serializable] public struct FloatRange : IEquatable<FloatRange>, IComparable<FloatRange> { public float min; public float max; public FloatRange(float min, float max) : this() { this.min = Mathf.Min(min, max); this.max = Mathf.Max(min, max); } public float size { get { return max - min; } } public float random { get { return Random.Range(min, max); } } public bool valid { get { return min <= max; } } public FloatRange Validate() { return valid ? this : new FloatRange { min = max, max = min }; } public bool Contains(float value) { return value >= min && value <= max; } public bool Contains(FloatRange range) { return Contains(min) && Contains(max); } public bool Intersects(FloatRange range) { return range.min < min && Contains(range.max) || range.max > max && Contains(range.min); } public FloatRange Apply(Func<float, float> function) { return new FloatRange { min = function(min), max = function(max)}; } public float Lerp(float alpha) { if (alpha < 0) return min; if (alpha > 1) return max; return alpha*max + (1 - alpha)*min; } public float SmoothstepInterpolation(float alpha) { if(alpha < 0) return min; if(alpha > 1) return max; alpha = alpha * alpha * (3 - 2 * alpha); return alpha * max + (1 - alpha) * min; } public int CompareTo(FloatRange other) { if (this > other) return 1; if (this < other) return -1; return 0; } public override bool Equals(object obj) { if (obj is FloatRange) { FloatRange rf = (FloatRange) obj; return min.Equals(rf.min) && max.Equals(rf.max); } return false; } public override int GetHashCode() { return min.GetHashCode() ^ max.GetHashCode(); } public override string ToString() { return string.Format("[{0}, {1}]", min, max); } public bool Equals(FloatRange other) { return min.Equals(other.min) && max.Equals(other.max); } public static bool operator ==(FloatRange a, FloatRange b) { return a.min.Equals(b.min) && a.max.Equals(b.max); } public static bool operator !=(FloatRange a, FloatRange b) { return !a.min.Equals(b.min) || !a.max.Equals(b.max); } public static bool operator <(FloatRange a, FloatRange b) { return a.min < b.min && a.max < b.max; } public static bool operator <=(FloatRange a, FloatRange b) { return a.min <= b.min && a.max <= b.max; } public static bool operator >(FloatRange a, FloatRange b) { return a.min > b.min && a.max > b.max; } public static bool operator >=(FloatRange a, FloatRange b) { return a.min >= b.min && a.max >= b.max; } public static FloatRange operator +(FloatRange a, FloatRange b) { return new FloatRange {min = a.min + b.min, max = a.max + b.max}; } public static FloatRange operator +(FloatRange a, float b) { return new FloatRange { min = a.min + b, max = a.max + b }; } public static FloatRange operator +(float a, FloatRange b) { return new FloatRange { min = b.min + a, max = b.max + a }; } public static FloatRange operator -(FloatRange a, FloatRange b) { return new FloatRange {min = a.min - b.min, max = a.max - b.max}; } public static FloatRange operator -(FloatRange a, float b) { return new FloatRange { min = a.min - b, max = a.max - b }; } public static FloatRange operator -(float a, FloatRange b) { return new FloatRange { min = b.min - a, max = b.max - a }; } public static FloatRange operator -(FloatRange a) { return new FloatRange { min = -a.max, max = -a.min }; } public static FloatRange operator *(FloatRange a, FloatRange b) { return new FloatRange {min = a.min*b.min, max = a.max * b.max }; } public static FloatRange operator *(FloatRange a, float b) { return new FloatRange { min = a.min * b, max = a.max * b }; } public static FloatRange operator *(float a, FloatRange b) { return new FloatRange { min = b.min * a, max = b.max * a }; } public static FloatRange operator /(FloatRange a, FloatRange b) { return new FloatRange {min = a.min / b.min, max = a.max / b.max}; } public static FloatRange operator /(FloatRange a, float b) { return new FloatRange { min = a.min / b, max = a.max / b }; } public static explicit operator IntRange(FloatRange value) { return new IntRange { min = (int)value.min, max = (int)value.max }; } } [Serializable] public struct IntRange : IEquatable<IntRange>, IComparable<IntRange> { public int min; public int max; public IntRange(int min, int max) : this() { this.min = Mathf.Min(min, max); this.max = Mathf.Max(min, max); } public float size { get { return max - min; } } public int random { get { return Random.Range(min, max + 1); } } public bool valid { get { return min <= max; } } public IntRange Validate() { return valid ? this : new IntRange {min = max, max = min}; } public bool Contains(int value) { return value >= min && value <= max; } public bool Contains(IntRange range) { return Contains(min) && Contains(max); } public bool Intersects(IntRange range) { return range.min < min && Contains(range.max) || range.max > max && Contains(range.min); } public IntRange Apply(Func<int, int> function) { return new IntRange { min = function(min), max = function(max) }; } public float Lerp(float alpha) { if(alpha < 0) return min; if(alpha > 1) return max; return alpha * max + (1 - alpha) * min; } public int LerpToInt(float alpha) { if(alpha < 0) return min; if(alpha > 1) return max; return Mathf.RoundToInt(alpha * max + (1 - alpha) * min); } public float SmoothstepInterpolation(float alpha) { if(alpha < 0) return min; if(alpha > 1) return max; alpha = alpha * alpha * (3 - 2 * alpha); return alpha * max + (1 - alpha) * min; } public float SmoothstepInterpolationToInt(float alpha) { if(alpha < 0) return min; if(alpha > 1) return max; alpha = alpha * alpha * (3 - 2 * alpha); return Mathf.RoundToInt(alpha * max + (1 - alpha) * min); } public int CompareTo(IntRange other) { if(this > other) return 1; if(this < other) return -1; return 0; } public override bool Equals(object obj) { if(obj is IntRange) { IntRange rf = (IntRange)obj; return min.Equals(rf.min) && max.Equals(rf.max); } return false; } public override int GetHashCode() { return min.GetHashCode() ^ max.GetHashCode(); } public override string ToString() { return string.Format("[{0}, {1}]", min, max); } public bool Equals(IntRange other) { return min.Equals(other.min) && max.Equals(other.max); } public static bool operator ==(IntRange a, IntRange b) { return a.min.Equals(b.min) && a.max.Equals(b.max); } public static bool operator !=(IntRange a, IntRange b) { return !a.min.Equals(b.min) || !a.max.Equals(b.max); } public static bool operator <(IntRange a, IntRange b) { return a.min < b.min && a.max < b.max; } public static bool operator <=(IntRange a, IntRange b) { return a.min <= b.min && a.max <= b.max; } public static bool operator >(IntRange a, IntRange b) { return a.min > b.min && a.max > b.max; } public static bool operator >=(IntRange a, IntRange b) { return a.min >= b.min && a.max >= b.max; } public static IntRange operator +(IntRange a, IntRange b) { return new IntRange { min = a.min + b.min, max = a.max + b.max }; } public static IntRange operator +(IntRange a, int b) { return new IntRange {min = a.min + b, max = a.max + b}; } public static IntRange operator +(int a, IntRange b) { return new IntRange { min = b.min + a, max = b.max + a }; } public static IntRange operator -(IntRange a, IntRange b) { return new IntRange { min = a.min - b.min, max = a.max - b.max }; } public static IntRange operator -(IntRange a, int b) { return new IntRange { min = a.min - b, max = a.max - b }; } public static IntRange operator -(int a, IntRange b) { return new IntRange { min = b.min - a, max = b.max - a }; } public static IntRange operator -(IntRange a) { return new IntRange {min = -a.max, max = -a.min}; } public static IntRange operator *(IntRange a, IntRange b) { return new IntRange { min = a.min * b.min, max = a.max * b.max }; } public static IntRange operator *(IntRange a, int b) { return new IntRange { min = a.min * b, max = a.max * b }; } public static IntRange operator *(int a, IntRange b) { return new IntRange { min = b.min * a, max = b.max * a }; } public static IntRange operator /(IntRange a, IntRange b) { return new IntRange { min = a.min / b.min, max = a.max / b.max }; } public static IntRange operator /(IntRange a, int b) { return new IntRange { min = a.min / b, max = a.max / b }; } public static implicit operator FloatRange(IntRange value) { return new FloatRange { min = value.min, max = value.max }; } } public class MinMaxRangeAttribute : Attribute { public MinMaxRangeAttribute(float min, float max) { this.min = min; this.max = max; } public float min { get; private set; } public float max { get; private set; } }
Comments