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