Passed
Push — linear-mapping ( a0bf6d...95faa6 )
by Konstantinos
01:33
created

LinearScale.__getitem__()   A

Complexity

Conditions 3

Size

Total Lines 7
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
nop 2
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
import attr
2
3
@attr.s
4
class LinearScale:
5
    lower_bound = attr.ib(init=True, type=int)
6
    upper_bound = attr.ib(init=True, type=int)
7
8
    @upper_bound.validator
9
    def __validate_scale(self, attribute, upper_bound):
10
        if upper_bound <= self.lower_bound:
11
            raise ValueError(
12
                f'The linear scale, should have lower_bound < upper_bound. Instead lower_bound={self.lower_bound}, upper_bound={upper_bound}')
13
14
    def __len__(self):
15
        """Returns 2, since the lower and upper bound values are sufficient to define a linear scale"""
16
        return 2
17
18
    @classmethod
19
    def create(cls, two_element_list_like):
20
        return LinearScale(*[_ for _ in two_element_list_like])
21
22
23
@attr.s
24
class MapOnLinearSpace:
25
    _from_scale = attr.ib(init=True, type=LinearScale)
26
    _target_scale = attr.ib(init=True, type=LinearScale)
27
    _reverse = attr.ib(init=True, default=False, type=bool)
28
    _transform_callback = attr.ib(init=False,
29
                                  default=attr.Factory(lambda self: self._get_transform_callback(), takes_self=True))
30
31
    @classmethod
32
    def universal_constructor(cls, from_scale, target_scale, reverse=False):
33
        return MapOnLinearSpace(LinearScale.create(from_scale),
34
                                LinearScale.create(target_scale),
35
                                reverse)
36
37
    def __transform_inverted(self, number):
38
        # F(x) = { ( to_scale_min - to_scale_max ) * x + to_scale_max * from_scale_max - to_scale_min * from_scale_min }  / ( from_scale_max - from_scale_min )
39
        return ( (self._target_scale.lower_bound - self._target_scale.upper_bound) * number + self._target_scale.upper_bound * self._from_scale.upper_bound - self._target_scale.lower_bound * self._from_scale.lower_bound ) / (
40
               self._from_scale.upper_bound - self._from_scale.lower_bound)
41
42
    def __transform(self, number):
43
        return ((self._target_scale.upper_bound - self._target_scale.lower_bound) * number + self._target_scale.lower_bound * self._from_scale.upper_bound - self._target_scale.lower_bound * self._from_scale.lower_bound) / (self._from_scale.upper_bound - self._from_scale.lower_bound)
44
45
    def _get_transform_callback(self):
46
        if self._reverse:
47
            return self.__transform_inverted
48
        return self.__transform
49
50
    def transform(self, number):
51
        """Transform the input number to a different linear scale"""
52
        return self._transform_callback(number)
53
54
    @property
55
    def from_scale(self):
56
        return self._from_scale
57
58
    @from_scale.setter
59
    def from_scale(self, from_scale):
60
        self._from_scale = LinearScale.create(from_scale)
61
        self._transform_callback = self._get_transform_callback()
62
63
    @property
64
    def target_scale(self):
65
        return self._target_scale
66
67
    @target_scale.setter
68
    def target_scale(self, target_scale):
69
        self._target_scale = LinearScale.create(target_scale)
70
        self._transform_callback = self._get_transform_callback()
71
72
    @property
73
    def reverse(self):
74
        return self._reverse
75
76
    @reverse.setter
77
    def reverse(self, reverse):
78
        self._reverse = reverse
79
        self._transform_callback = self._get_transform_callback()
80