Skip to content

Commit 9b7ef8d

Browse files
MuximizeMarnixRijnartangularsen
authored
Add UnitMath.Clamp(value, min, max) (#1157)
* Add UnitMath.Clamp(value, min, max) * Change Clamp to convert min/max to the unit of value, following the principle of least surprise. * Update UnitMath.cs Co-authored-by: Marnix <[email protected]> Co-authored-by: Andreas Gullberg Larsen <[email protected]>
1 parent afe8ef2 commit 9b7ef8d

File tree

2 files changed

+76
-0
lines changed

2 files changed

+76
-0
lines changed

UnitsNet.Tests/UnitMathTests.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,5 +318,40 @@ public void SumOfLengthsWithSelectorCalculatesCorrectly()
318318
Assert.Equal(150, sum.Value);
319319
Assert.Equal(LengthUnit.Centimeter, sum.Unit);
320320
}
321+
322+
[Fact]
323+
public void ClampCalculatesCorrectly()
324+
{
325+
var min = Length.FromMeters(-1);
326+
var max = Length.FromCentimeters(150);
327+
328+
var value1 = Length.FromMillimeters(33);
329+
330+
Length clampedValue = UnitMath.Clamp(value1, min, max);
331+
Assert.Equal(33, clampedValue.Value);
332+
Assert.Equal(LengthUnit.Millimeter, clampedValue.Unit);
333+
334+
var value2 = Length.FromMillimeters(-1500);
335+
336+
Length clampedMin = UnitMath.Clamp(value2, min, max);
337+
Assert.Equal(-1000, clampedMin.Value);
338+
Assert.Equal(LengthUnit.Millimeter, clampedMin.Unit);
339+
340+
var value3 = Length.FromMillimeters(2000);
341+
342+
Length clampedMax = UnitMath.Clamp(value3, min, max);
343+
Assert.Equal(1500, clampedMax.Value);
344+
Assert.Equal(LengthUnit.Millimeter, clampedMax.Unit);
345+
}
346+
347+
[Fact]
348+
public void ClampMinGreaterThanMaxThrowsException()
349+
{
350+
var min = Length.FromMeters(2);
351+
var max = Length.FromCentimeters(150);
352+
var value = Length.FromMillimeters(33);
353+
354+
Assert.Throws<ArgumentException>(() => UnitMath.Clamp(value, min, max));
355+
}
321356
}
322357
}

UnitsNet/UnitMath.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,47 @@ public static TQuantity Average<TSource, TQuantity>(this IEnumerable<TSource> so
198198
return source.Select(selector).Average(unitType);
199199
}
200200

201+
/// <summary>Returns <paramref name="value" /> clamped to the inclusive range of <paramref name="min" /> and <paramref name="max" />.</summary>
202+
/// <param name="value">The value to be clamped.</param>
203+
/// <param name="min">The lower bound of the result.</param>
204+
/// <param name="max">The upper bound of the result.</param>
205+
/// <returns>
206+
/// <paramref name="value" /> if <paramref name="min" /> ≤ <paramref name="value" /> ≤ <paramref name="max" />.
207+
///
208+
/// -or-
209+
///
210+
/// <paramref name="min" /> (converted to value.Unit) if <paramref name="value" /> &lt; <paramref name="min" />.
211+
///
212+
/// -or-
213+
///
214+
/// <paramref name="max" /> (converted to value.Unit) if <paramref name="max" /> &lt; <paramref name="value" />.
215+
/// </returns>
216+
/// <exception cref="ArgumentException">
217+
/// <paramref name="min" /> cannot be greater than <paramref name="max" />.
218+
/// </exception>
219+
public static TQuantity Clamp<TQuantity>(TQuantity value, TQuantity min, TQuantity max) where TQuantity : IComparable, IQuantity
220+
{
221+
var minValue = (TQuantity)min.ToUnit(value.Unit);
222+
var maxValue = (TQuantity)max.ToUnit(value.Unit);
223+
224+
if (minValue.CompareTo(maxValue) > 0)
225+
{
226+
throw new ArgumentException($"min ({min}) cannot be greater than max ({max})", nameof(min));
227+
}
228+
229+
if (value.CompareTo(minValue) < 0)
230+
{
231+
return minValue;
232+
}
233+
234+
if (value.CompareTo(maxValue) > 0)
235+
{
236+
return maxValue;
237+
}
238+
239+
return value;
240+
}
241+
201242
/// <summary>
202243
/// Explicitly create a <see cref="QuantityValue"/> instance from a double
203244
/// </summary>

0 commit comments

Comments
 (0)