| 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 |  |  |  |