using System;
using Chernobyl.Utility;
namespace Chernobyl.Mathematics
{
///
/// Mathematics dealing with ranges
///
public static class Range
{
///
/// The range between 0 and 1.
///
public static readonly Tuple Unit = new Tuple(0.0, 1.0);
///
/// Linearly normalizes a value to a new range based on the range it is currently bound to.
///
/// The current value bounded by .
/// The current inclusive min () and
/// inclusive max () range of .
/// The inclusive min () and
/// inclusive max () range of the result.
/// modified to be bound to .
public static double Normalize(this double value, Tuple oldRange, Tuple newRange)
{
// This implementation was taken from https://stats.stackexchange.com/a/70808 and the
// answer's comments.
var oldMin = oldRange.Item1;
var oldMax = oldRange.Item2;
value.Throw(nameof(value)).IfLessThan(oldMin, $"{nameof(oldRange)} min").IfGreaterThan(oldMax, $"{nameof(oldRange)} max");
oldMin.Throw($"{nameof(oldRange)} min").IfGreaterThanOrEqualTo(oldMax, $"{nameof(oldRange)} max");
var newMin = newRange.Item1;
var newMax = newRange.Item2;
newMin.Throw($"{nameof(newRange)} min").IfGreaterThanOrEqualTo(newMax, $"{nameof(newRange)} max");
var a = (newMax - newMin) / (oldMax - oldMin);
var b = newMax - a * oldMax;
// Decimal precision issues can sometimes cause the calculated value to be slightly
// above or below the new min/max. So we clamp the value to prevent this.
return (a * value + b).Clamp(newMin, newMax);
}
}
}