Skip to content

Commit 5f38265

Browse files
authored
Merge pull request #2 from lesamouraipourpre/unconstrained-map-range
Add a constrained parameter to map_range()
2 parents cdf9944 + 0218b0f commit 5f38265

5 files changed

+135
-22
lines changed

adafruit_simplemath.py

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# SPDX-FileCopyrightText: Copyright (c) 2021 Dan Halbert for Adafruit Industries LLC
2+
# SPDX-FileCopyrightText: 2021 James Carr
23
#
34
# SPDX-License-Identifier: MIT
45
"""
@@ -8,7 +9,7 @@
89
Math utility functions
910
1011
11-
* Author(s): Adafruit Industries
12+
* Author(s): Dan Halbert, James Carr
1213
1314
Implementation Notes
1415
--------------------
@@ -27,12 +28,59 @@ def map_range(
2728
x: float, in_min: float, in_max: float, out_min: float, out_max: float
2829
) -> float:
2930
"""
30-
Maps a number from one range to another. Somewhat similar to the Arduino ``map()`` function,
31-
but returns a floating point result, and constrains the output value to be between
32-
``out_min`` and ``out_max``.
33-
If ``in_min`` is greater than ``in_max`` or ``out_min`` is greater than ``out_max``,
34-
the corresponding range is reversed, allowing, for example, mapping a range of 0-10 to 50-0.
31+
Maps a number from one range to another. Somewhat similar to the Arduino
32+
:attr:`map()` function, but returns a floating point result, and
33+
constrains the output value to be between :attr:`out_min` and
34+
:attr:`out_max`. If :attr:`in_min` is greater than :attr:`in_max` or
35+
:attr:`out_min` is greater than :attr:`out_max`, the corresponding range
36+
is reversed, allowing, for example, mapping a range of 0-10 to 50-0.
3537
38+
See also :py:func:`map_unconstrained_range`
39+
40+
.. code-block::
41+
42+
from adafruit_simplemath import map_range
43+
44+
percent = 23
45+
screen_width = 320 # or board.DISPLAY.width
46+
x = map_range(percent, 0, 100, 0, screen_width - 1)
47+
print("X position", percent, "% from the left of screen is", x)
48+
49+
:param float x: Value to convert
50+
:param float in_min: Start value of input range.
51+
:param float in_max: End value of input range.
52+
:param float out_min: Start value of output range.
53+
:param float out_max: End value of output range.
54+
:return: Returns value mapped to new range.
55+
:rtype: float
56+
"""
57+
58+
mapped = map_unconstrained_range(x, in_min, in_max, out_min, out_max)
59+
return constrain(mapped, out_min, out_max)
60+
61+
62+
def map_unconstrained_range(
63+
x: float, in_min: float, in_max: float, out_min: float, out_max: float
64+
) -> float:
65+
"""
66+
Maps a number from one range to another. Somewhat similar to the Arduino
67+
:attr:`map()` function, but returns a floating point result, and
68+
does not constrain the output value to be between :attr:`out_min` and
69+
:attr:`out_max`. If :attr:`in_min` is greater than :attr:`in_max` or
70+
:attr:`out_min` is greater than :attr:`out_max`, the corresponding range
71+
is reversed, allowing, for example, mapping a range of 0-10 to 50-0.
72+
73+
See also :py:func:`map_range`
74+
75+
.. code-block::
76+
77+
from adafruit_simplemath import map_unconstrained_range
78+
79+
celsius = -20
80+
fahrenheit = map_unconstrained_range(celsius, 0, 100, 32, 212)
81+
print(celsius, "degress Celsius =", fahrenheit, "degrees Fahrenheit")
82+
83+
:param float x: Value to convert
3684
:param float in_min: Start value of input range.
3785
:param float in_max: End value of input range.
3886
:param float out_min: Start value of output range.
@@ -50,22 +98,26 @@ def map_range(
5098
mapped = 0.5
5199
mapped *= out_max - out_min
52100
mapped += out_min
53-
if out_min <= out_max:
54-
return max(min(mapped, out_max), out_min)
55-
return min(max(mapped, out_max), out_min)
101+
102+
return mapped
56103

57104

58105
def constrain(x: float, out_min: float, out_max: float) -> float:
59-
"""Constrains ``x`` to be within the inclusive range [``out_min``, ``out_max``].
60-
Sometimes called ``clip`` or ``clamp`` in other libraries.
61-
``out_min`` should be less than or equal to ``out_max``.
62-
If ``x`` is less than ``out_min``, return ``out_min``.
63-
If ``x`` is greater than ``out_max``, return ``out_max``.
64-
Otherwise just return ``x``.
106+
"""Constrains :attr:`x` to be within the inclusive range
107+
[:attr:`out_min`, :attr:`out_max`]. Sometimes called :attr:`clip` or
108+
:attr:`clamp` in other libraries. :attr:`out_min` should be less than or
109+
equal to :attr:`out_max`.
110+
If :attr:`x` is less than :attr:`out_min`, return :attr:`out_min`.
111+
If :attr:`x` is greater than :attr:`out_max`, return :attr:`out_max`.
112+
Otherwise just return :attr:`x`.
113+
If :attr:`max_value` is less than :attr:`min_value`, they will be swapped.
65114
115+
:param float x: Value to constrain
66116
:param float out_min: Lower bound of output range.
67117
:param float out_max: Upper bound of output range.
68118
:return: Returns value constrained to given range.
69119
:rtype: float
70120
"""
71-
return max(out_min, min(x, out_max))
121+
if out_min <= out_max:
122+
return max(min(x, out_max), out_min)
123+
return min(max(x, out_max), out_min)

examples/simplemath_simpletest.py

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,54 @@
1-
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
1+
# SPDX-FileCopyrightText: 2021 Dan Halbert for Adafruit Industries
2+
# SPDX-FileCopyrightText: 2021 James Carr
23
#
34
# SPDX-License-Identifier: Unlicense
45

5-
from adafruit_simplemath import map_range, constrain
6+
from adafruit_simplemath import map_range, map_unconstrained_range, constrain
67

8+
print("map_range() examples")
79
# Map, say, a sensor value, from a range of 0-255 to 0-1023.
8-
print(map_range(30, 0, 255, 0, 1023))
10+
sensor_input_value = 30
11+
sensor_converted_value = map_range(sensor_input_value, 0, 255, 0, 1023)
12+
print(
13+
"Sensor input value:",
14+
sensor_input_value,
15+
"Converted value:",
16+
sensor_converted_value,
17+
)
918

19+
percent = 23
20+
screen_width = 320 # or board.DISPLAY.width
21+
x = map_range(percent, 0, 100, 0, screen_width - 1)
22+
print("X position", percent, "% from the left of screen is", x)
23+
24+
print("\nmap_unconstrained_range() examples")
25+
celsius = 20
26+
fahrenheit = map_unconstrained_range(celsius, 0, 100, 32, 212)
27+
print(celsius, "degress Celsius =", fahrenheit, "degrees Fahrenheit")
28+
29+
celsius = -20
30+
fahrenheit = map_unconstrained_range(celsius, 0, 100, 32, 212)
31+
print(celsius, "degress Celsius =", fahrenheit, "degrees Fahrenheit")
32+
33+
print("\nconstrain() examples")
1034
# Constrain a value to a range.
11-
print(constrain(0, 1, 3)) # prints 1
12-
print(constrain(4, 1, 3)) # prints 3
13-
print(constrain(2, 2, 3)) # prints 2
35+
def constrain_example(value, min_value, max_value):
36+
constrained_value = constrain(value, min_value, max_value)
37+
print(
38+
"Constrain",
39+
value,
40+
"between [",
41+
min_value,
42+
"and",
43+
max_value,
44+
"] gives",
45+
constrained_value,
46+
)
47+
48+
49+
constrain_example(0, 1, 3) # expects 1
50+
constrain_example(0, 3, 1) # expects 1
51+
constrain_example(4, 1, 3) # expects 3
52+
constrain_example(4, 3, 1) # expects 3
53+
constrain_example(2, 2, 3) # expects 2
54+
constrain_example(2, 3, 2) # expects 2

tests/constrain_test.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# SPDX-FileCopyrightText: 2021 Dan Halbert for Adafruit Industries
2+
# SPDX-FileCopyrightText: 2021 James Carr
23
#
34
# SPDX-License-Identifier: Unlicense
45

@@ -10,3 +11,8 @@ def test_constrain():
1011
assert constrain(10, 1, 10) == 10
1112
assert constrain(0, 1, 10) == 1
1213
assert constrain(11, 1, 10) == 10
14+
15+
# Check out_min > out_max
16+
assert constrain(5, 10, 0) == 5
17+
assert constrain(-5, 10, 0) == 0
18+
assert constrain(15, 10, 0) == 10

tests/map_range_test.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@ def test_map_range():
1313
assert map_range(1, 10, 0, 0, 5) == 4.5
1414
assert map_range(1, 0, 10, 10, 0) == 9.0
1515
assert map_range(10, 1, 10, 1, 20) == 20.0
16+
# Tests for out-of-range descending output order
17+
assert map_range(11, 1, 10, 20, 1) == 1.0
18+
assert map_range(-1, 1, 10, 20, 1) == 20.0

tests/map_unconstrained_range_test.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# SPDX-FileCopyrightText: 2021 James Carr
2+
#
3+
# SPDX-License-Identifier: Unlicense
4+
5+
from adafruit_simplemath import map_unconstrained_range
6+
7+
8+
def test_map_unconstrained_range():
9+
assert map_unconstrained_range(-40, 32, 212, 0, 100) == -40.0
10+
assert map_unconstrained_range(50, 32, 212, 0, 100) == 10.0
11+
assert map_unconstrained_range(392, 32, 212, 0, 100) == 200.0

0 commit comments

Comments
 (0)