55
66import numpy as np
77
8- from ._colormap import ColorMap
9- from .hsluv import hex_to_rgb , rgb_to_hex
10- from .named_colors import get_named_color
8+ from ..hsluv import hex_to_rgb , rgb_to_hex
9+ from ._colormap import ColorMap , ColorMapKind
1110
1211if typing .TYPE_CHECKING :
1312 from typing import Optional , Sequence
2524INNER_SPACE256 = SPACE256 [1 :- 1 ]
2625ROUNDING_JITTER = 1e-12
2726
28- __all__ = ("GradientMap" ,)
27+
28+ class _InterpolatedGen (ColorMap ):
29+ _r_lookup : NDArrayFloat
30+ _g_lookup : NDArrayFloat
31+ _b_lookup : NDArrayFloat
32+
33+ def _generate_colors (self , x : FloatArrayLike ) -> Sequence [RGBHexColor ]:
34+ """
35+ Lookup colors in the interpolated ranges
36+
37+ Parameters
38+ ----------
39+ x :
40+ Values in the range [0, 1]. O maps to the start of the
41+ gradient, and 1 to the end of the gradient.
42+ """
43+ x = np .asarray (x )
44+ idx = np .round ((x * 255 ) + ROUNDING_JITTER ).astype (int )
45+ arr = np .column_stack (
46+ [self ._r_lookup [idx ], self ._g_lookup [idx ], self ._b_lookup [idx ]],
47+ )
48+ return [rgb_to_hex (c ) for c in arr ]
2949
3050
3151@dataclass
32- class GradientMap ( ColorMap ):
52+ class InterpolatedMap ( _InterpolatedGen ):
3353 colors : Sequence [RGBHexColor ] | Sequence [RGBColor ] | RGBColorArray
3454 values : Optional [Sequence [float ]] = None
55+ kind : ColorMapKind = ColorMapKind .miscellaneous
3556
3657 def __post_init__ (self ):
58+ from ..named_colors import get_named_color
59+
3760 if self .values is None :
3861 values = np .linspace (0 , 1 , len (self .colors ))
3962 elif len (self .colors ) < 2 :
@@ -66,25 +89,12 @@ def __post_init__(self):
6689 self ._g_lookup = interp_lookup (values , self ._data [:, 1 ])
6790 self ._b_lookup = interp_lookup (values , self ._data [:, 2 ])
6891
69- def _generate_colors (self , x : FloatArrayLike ) -> Sequence [RGBHexColor ]:
70- """
71- Lookup colors in the interpolated ranges
7292
73- Parameters
74- ----------
75- x :
76- Values in the range [0, 1]. O maps to the start of the
77- gradient, and 1 to the end of the gradient.
78- """
79- x = np .asarray (x )
80- idx = np .round ((x * 255 ) + ROUNDING_JITTER ).astype (int )
81- arr = np .column_stack (
82- [self ._r_lookup [idx ], self ._g_lookup [idx ], self ._b_lookup [idx ]]
83- )
84- return [rgb_to_hex (c ) for c in arr ]
85-
86-
87- def interp_lookup (x : NDArrayFloat , values : NDArrayFloat ) -> NDArrayFloat :
93+ def interp_lookup (
94+ x : NDArrayFloat ,
95+ values : NDArrayFloat ,
96+ values_alt : Optional [NDArrayFloat ] = None ,
97+ ) -> NDArrayFloat :
8898 """
8999 Create an interpolation lookup array
90100
@@ -98,7 +108,25 @@ def interp_lookup(x: NDArrayFloat, values: NDArrayFloat) -> NDArrayFloat:
98108 should be sorted.
99109 values:
100110 In the range [0, 1]. Must be the same length as x.
111+ values_alt:
112+ In the range [0, 1]. Must be the same length as x.
113+ Makes it possible to have adjacent interpolation regions
114+ that with gaps in them numbers. e.g.
115+
116+ values = [0, 0.1, 0.5, 1]
117+ values_alt = [0, 0.1, 0.6, 1]
118+
119+ Creates the regions
120+
121+ [(0, 0.1), (0.1, 0.5), (0.6, 1)]
122+
123+ If values_alt is None the region would be
124+
125+ [(0, 0.1), (0.1, 0.5), (0.5, 1)]
101126 """
127+ if values_alt is None :
128+ values_alt = values
129+
102130 # - Map x from [0, 1] onto [0, 255] i.e. the color channel
103131 # breaks (continuous)
104132 # - Find where x would be mapped onto the grid (discretizing)
@@ -111,10 +139,12 @@ def interp_lookup(x: NDArrayFloat, values: NDArrayFloat) -> NDArrayFloat:
111139 ind = np .searchsorted (x256 , SPACE256 )[1 :- 1 ]
112140 ind_prev = ind - 1
113141 distance = (INNER_SPACE256 - x256 [ind_prev ]) / (x256 [ind ] - x256 [ind_prev ])
142+ stop = values [ind ]
143+ start = values_alt [ind_prev ]
114144 lut = np .concatenate (
115145 [
116146 [values [0 ]],
117- distance * (values [ ind ] - values [ ind_prev ]) + values [ ind_prev ] ,
147+ start + distance * (stop - start ) ,
118148 [values [- 1 ]],
119149 ]
120150 )
0 commit comments