Skip to content

Commit f4142be

Browse files
* Auto-swap the limits passed to constrain if necessary (Closes #3)
* Split map_range into map_range and unconstrained_map_range. * map_range will constrain the output value within the provided limits * unconstrained_map_range will only use the limits to define the linear equation * Add tests for constrain with auto-swap * Add tests for unconstrained_map_range
1 parent dbc4877 commit f4142be

File tree

5 files changed

+81
-29
lines changed

5 files changed

+81
-29
lines changed

adafruit_simplemath.py

Lines changed: 54 additions & 21 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
--------------------
@@ -24,21 +25,18 @@
2425

2526

2627
def map_range(
27-
x: float,
28-
in_min: float,
29-
in_max: float,
30-
out_min: float,
31-
out_max: float,
32-
constrained: bool = True,
28+
x: float, in_min: float, in_max: float, out_min: float, out_max: float
3329
) -> float:
3430
"""
3531
Maps a number from one range to another. Somewhat similar to the Arduino
3632
:attr:`map()` function, but returns a floating point result, and
37-
optionally constrains the output value to be between :attr:`out_min` and
33+
constrains the output value to be between :attr:`out_min` and
3834
:attr:`out_max`. If :attr:`in_min` is greater than :attr:`in_max` or
3935
:attr:`out_min` is greater than :attr:`out_max`, the corresponding range
4036
is reversed, allowing, for example, mapping a range of 0-10 to 50-0.
4137
38+
See also :py:func:`unconstrained_map_range`
39+
4240
.. code-block::
4341
4442
from adafruit_simplemath import map_range
@@ -48,25 +46,61 @@ def map_range(
4846
x = map_range(percent, 0, 100, 0, screen_width - 1)
4947
print("X position", percent, "% from the left of screen is", x)
5048
51-
celsius = 20
52-
fahrenheit = map_range(celsius, 0, 100, 32, 212, constrained=False)
53-
print(celsius, "degress Celsius =", fahrenheit, "degrees Fahrenheit")
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+
# in_range = in_max - in_min
58+
# in_delta = x - in_min
59+
# if in_range != 0:
60+
# mapped = in_delta / in_range
61+
# elif in_delta != 0:
62+
# mapped = in_delta
63+
# else:
64+
# mapped = 0.5
65+
# mapped *= out_max - out_min
66+
# mapped += out_min
67+
68+
mapped = unconstrained_map_range(x, in_min, in_max, out_min, out_max)
69+
70+
if out_min <= out_max:
71+
return max(min(mapped, out_max), out_min)
72+
return min(max(mapped, out_max), out_min)
73+
74+
75+
def unconstrained_map_range(
76+
x: float, in_min: float, in_max: float, out_min: float, out_max: float
77+
) -> float:
78+
"""
79+
Maps a number from one range to another. Somewhat similar to the Arduino
80+
:attr:`map()` function, but returns a floating point result, and
81+
does not constrain the output value to be between :attr:`out_min` and
82+
:attr:`out_max`. If :attr:`in_min` is greater than :attr:`in_max` or
83+
:attr:`out_min` is greater than :attr:`out_max`, the corresponding range
84+
is reversed, allowing, for example, mapping a range of 0-10 to 50-0.
85+
86+
See also :py:func:`map_range`
87+
88+
.. code-block::
89+
90+
from adafruit_simplemath import unconstrained_map_range
5491
5592
celsius = -20
56-
fahrenheit = map_range(celsius, 0, 100, 32, 212, False)
93+
fahrenheit = unconstrained_map_range(celsius, 0, 100, 32, 212)
5794
print(celsius, "degress Celsius =", fahrenheit, "degrees Fahrenheit")
5895
5996
:param float x: Value to convert
6097
:param float in_min: Start value of input range.
6198
:param float in_max: End value of input range.
6299
:param float out_min: Start value of output range.
63100
:param float out_max: End value of output range.
64-
:param bool constrained: Whether the output value should be constrained
65-
between :attr:`out_min` and :attr:`out_max`. Defaults to `True`.
66101
:return: Returns value mapped to new range.
67102
:rtype: float
68103
"""
69-
# pylint: disable=too-many-arguments
70104
in_range = in_max - in_min
71105
in_delta = x - in_min
72106
if in_range != 0:
@@ -78,11 +112,7 @@ def map_range(
78112
mapped *= out_max - out_min
79113
mapped += out_min
80114

81-
if not constrained:
82-
return mapped
83-
if out_min <= out_max:
84-
return max(min(mapped, out_max), out_min)
85-
return min(max(mapped, out_max), out_min)
115+
return mapped
86116

87117

88118
def constrain(x: float, out_min: float, out_max: float) -> float:
@@ -93,11 +123,14 @@ def constrain(x: float, out_min: float, out_max: float) -> float:
93123
If :attr:`x` is less than :attr:`out_min`, return :attr:`out_min`.
94124
If :attr:`x` is greater than :attr:`out_max`, return :attr:`out_max`.
95125
Otherwise just return :attr:`x`.
126+
If :attr:`max_value` is less than :attr:`min_value`, they will be swapped.
96127
97128
:param float x: Value to constrain
98129
:param float out_min: Lower bound of output range.
99130
:param float out_max: Upper bound of output range.
100131
:return: Returns value constrained to given range.
101132
:rtype: float
102133
"""
103-
return max(out_min, min(x, out_max))
134+
if out_min <= out_max:
135+
return max(min(x, out_max), out_min)
136+
return min(max(x, out_max), out_min)

examples/simplemath_simpletest.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
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, unconstrained_map_range, constrain
67

78
print("map_range() examples")
89
# Map, say, a sensor value, from a range of 0-255 to 0-1023.
@@ -20,15 +21,16 @@
2021
x = map_range(percent, 0, 100, 0, screen_width - 1)
2122
print("X position", percent, "% from the left of screen is", x)
2223

24+
print("\nunconstrained_map_range() examples")
2325
celsius = 20
24-
fahrenheit = map_range(celsius, 0, 100, 32, 212, constrained=False)
26+
fahrenheit = unconstrained_map_range(celsius, 0, 100, 32, 212)
2527
print(celsius, "degress Celsius =", fahrenheit, "degrees Fahrenheit")
2628

2729
celsius = -20
28-
fahrenheit = map_range(celsius, 0, 100, 32, 212, False)
30+
fahrenheit = unconstrained_map_range(celsius, 0, 100, 32, 212)
2931
print(celsius, "degress Celsius =", fahrenheit, "degrees Fahrenheit")
3032

31-
print("constrain() examples")
33+
print("\nconstrain() examples")
3234
# Constrain a value to a range.
3335
def constrain_example(value, min_value, max_value):
3436
constrained_value = constrain(value, min_value, max_value)
@@ -45,5 +47,8 @@ def constrain_example(value, min_value, max_value):
4547

4648

4749
constrain_example(0, 1, 3) # expects 1
50+
constrain_example(0, 3, 1) # expects 1
4851
constrain_example(4, 1, 3) # expects 3
52+
constrain_example(4, 3, 1) # expects 3
4953
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: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,3 @@ 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-
assert map_range(392, 32, 212, 0, 100) == 100.0
17-
assert map_range(392, 32, 212, 0, 100, constrained=True) == 100.0
18-
assert map_range(392, 32, 212, 0, 100, constrained=False) == 200.0
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 unconstrained_map_range
6+
7+
8+
def test_unconstrained_map_range():
9+
assert unconstrained_map_range(-40, 32, 212, 0, 100) == -40.0
10+
assert unconstrained_map_range(50, 32, 212, 0, 100) == 10.0
11+
assert unconstrained_map_range(392, 32, 212, 0, 100) == 200.0

0 commit comments

Comments
 (0)