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); } } }