Skip to main content

xpict_core/
colormap.rs

1//! Vendored xenosite colormap (from xenopict._cm_def / Python `_xenosite_cm`).
2//!
3//! Rainbow sequential LUT: white → blue → green → yellow → orange-red.
4
5pub const XENOSITE: [[f64; 3]; 256] = [
6    [1.0, 1.0, 1.0],
7    [0.9849, 0.9907, 1.0],
8    [0.9699, 0.9814, 1.0],
9    [0.9548, 0.9721, 1.0],
10    [0.9399, 0.9627, 1.0],
11    [0.9249, 0.9534, 1.0],
12    [0.9099, 0.944, 1.0],
13    [0.8988, 0.937, 1.0],
14    [0.8839, 0.9277, 1.0],
15    [0.869, 0.9183, 1.0],
16    [0.8541, 0.9089, 1.0],
17    [0.8393, 0.8996, 1.0],
18    [0.8245, 0.8902, 1.0],
19    [0.8098, 0.8808, 1.0],
20    [0.7951, 0.8714, 1.0],
21    [0.7804, 0.8619, 1.0],
22    [0.7657, 0.8525, 1.0],
23    [0.751, 0.843, 1.0],
24    [0.7364, 0.8336, 1.0],
25    [0.7254, 0.8265, 1.0],
26    [0.7109, 0.817, 1.0],
27    [0.6963, 0.8075, 1.0],
28    [0.6818, 0.798, 1.0],
29    [0.6673, 0.7884, 1.0],
30    [0.6528, 0.7789, 1.0],
31    [0.6384, 0.7693, 1.0],
32    [0.624, 0.7597, 1.0],
33    [0.6096, 0.7501, 1.0],
34    [0.5952, 0.7404, 1.0],
35    [0.5809, 0.7307, 1.0],
36    [0.5666, 0.721, 1.0],
37    [0.5559, 0.7137, 1.0],
38    [0.5416, 0.704, 1.0],
39    [0.5274, 0.6942, 1.0],
40    [0.5132, 0.6844, 1.0],
41    [0.499, 0.6745, 1.0],
42    [0.4849, 0.6647, 1.0],
43    [0.4707, 0.6547, 1.0],
44    [0.4566, 0.6448, 1.0],
45    [0.4426, 0.6348, 1.0],
46    [0.4285, 0.6247, 1.0],
47    [0.4145, 0.6146, 1.0],
48    [0.4005, 0.6045, 1.0],
49    [0.39, 0.5968, 1.0],
50    [0.3761, 0.5866, 1.0],
51    [0.3622, 0.5763, 1.0],
52    [0.3483, 0.5659, 1.0],
53    [0.3344, 0.5554, 1.0],
54    [0.3206, 0.5449, 1.0],
55    [0.3068, 0.5343, 1.0],
56    [0.293, 0.5236, 1.0],
57    [0.2793, 0.5128, 1.0],
58    [0.2656, 0.502, 1.0],
59    [0.252, 0.491, 1.0],
60    [0.2384, 0.4799, 1.0],
61    [0.2282, 0.4715, 1.0],
62    [0.2148, 0.4602, 1.0],
63    [0.2014, 0.4487, 1.0],
64    [0.188, 0.4371, 1.0],
65    [0.1748, 0.4253, 1.0],
66    [0.1617, 0.4133, 1.0],
67    [0.1488, 0.4012, 1.0],
68    [0.1361, 0.3888, 1.0],
69    [0.1237, 0.3761, 1.0],
70    [0.1116, 0.3632, 1.0],
71    [0.1, 0.35, 1.0],
72    [0.0718, 0.3632, 0.9874],
73    [0.0437, 0.3728, 0.978],
74    [0.0006, 0.3848, 0.9657],
75    [0.0, 0.3942, 0.9557],
76    [0.0, 0.4034, 0.9457],
77    [0.0, 0.4124, 0.9356],
78    [0.0, 0.4212, 0.9253],
79    [0.0, 0.4299, 0.915],
80    [0.0, 0.4385, 0.9045],
81    [0.0, 0.4469, 0.894],
82    [0.0, 0.4552, 0.8834],
83    [0.0, 0.4633, 0.8726],
84    [0.0, 0.4714, 0.8618],
85    [0.0, 0.4773, 0.8536],
86    [0.0, 0.4852, 0.8426],
87    [0.0, 0.4929, 0.8315],
88    [0.0, 0.5006, 0.8203],
89    [0.0, 0.5082, 0.809],
90    [0.0, 0.5157, 0.7976],
91    [0.0, 0.5231, 0.7862],
92    [0.0, 0.5304, 0.7746],
93    [0.0, 0.5376, 0.7629],
94    [0.0, 0.5448, 0.7512],
95    [0.0, 0.5519, 0.7393],
96    [0.0, 0.5589, 0.7274],
97    [0.0, 0.5659, 0.7153],
98    [0.0, 0.571, 0.7063],
99    [0.0, 0.5779, 0.694],
100    [0.0, 0.5847, 0.6817],
101    [0.0, 0.5914, 0.6693],
102    [0.0, 0.5981, 0.6568],
103    [0.0, 0.6047, 0.6442],
104    [0.0, 0.6113, 0.6315],
105    [0.0, 0.6178, 0.6187],
106    [0.0, 0.6243, 0.6057],
107    [0.0, 0.6307, 0.5926],
108    [0.0, 0.6371, 0.5795],
109    [0.0, 0.6434, 0.5662],
110    [0.0, 0.6482, 0.5561],
111    [0.0, 0.6544, 0.5426],
112    [0.0, 0.6607, 0.5289],
113    [0.0, 0.6668, 0.515],
114    [0.0, 0.673, 0.501],
115    [0.0, 0.6791, 0.4868],
116    [0.0, 0.6852, 0.4724],
117    [0.0, 0.6913, 0.4578],
118    [0.0, 0.6973, 0.443],
119    [0.0, 0.7033, 0.428],
120    [0.0, 0.7092, 0.4126],
121    [0.0, 0.7152, 0.397],
122    [0.0, 0.7196, 0.385],
123    [0.0, 0.7255, 0.3687],
124    [0.0, 0.7313, 0.352],
125    [0.0, 0.7372, 0.3348],
126    [0.0, 0.743, 0.317],
127    [0.0, 0.7488, 0.2985],
128    [0.0, 0.7545, 0.2791],
129    [0.0, 0.7603, 0.2587],
130    [0.0, 0.766, 0.2371],
131    [0.0, 0.7717, 0.2137],
132    [0.0, 0.7774, 0.188],
133    [0.0, 0.7831, 0.159],
134    [0.0, 0.7873, 0.1337],
135    [0.0, 0.793, 0.0916],
136    [0.0, 0.7986, 0.0222],
137    [0.0686, 0.8001, 0.0],
138    [0.1211, 0.8002, 0.0],
139    [0.1579, 0.8004, 0.0],
140    [0.1876, 0.8005, 0.0],
141    [0.2131, 0.8006, 0.0],
142    [0.2357, 0.8008, 0.0],
143    [0.2562, 0.8009, 0.0],
144    [0.2751, 0.801, 0.0],
145    [0.2927, 0.8011, 0.0],
146    [0.3052, 0.8012, 0.0],
147    [0.321, 0.8013, 0.0],
148    [0.3361, 0.8014, 0.0],
149    [0.3505, 0.8015, 0.0],
150    [0.3643, 0.8015, 0.0],
151    [0.3775, 0.8016, 0.0],
152    [0.3903, 0.8017, 0.0],
153    [0.4027, 0.8018, 0.0],
154    [0.4147, 0.8018, 0.0],
155    [0.4263, 0.8019, 0.0],
156    [0.4377, 0.802, 0.0],
157    [0.4487, 0.802, 0.0],
158    [0.4568, 0.8021, 0.0],
159    [0.4674, 0.8021, 0.0],
160    [0.4778, 0.8022, 0.0],
161    [0.488, 0.8022, 0.0],
162    [0.4979, 0.8022, 0.0],
163    [0.5077, 0.8023, 0.0],
164    [0.5173, 0.8023, 0.0],
165    [0.5267, 0.8023, 0.0],
166    [0.536, 0.8023, 0.0],
167    [0.5451, 0.8023, 0.0],
168    [0.5541, 0.8024, 0.0],
169    [0.563, 0.8024, 0.0],
170    [0.5696, 0.8024, 0.0],
171    [0.5782, 0.8023, 0.0],
172    [0.5868, 0.8023, 0.0],
173    [0.5952, 0.8023, 0.0],
174    [0.6035, 0.8023, 0.0],
175    [0.6118, 0.8023, 0.0],
176    [0.6199, 0.8023, 0.0],
177    [0.6279, 0.8022, 0.0],
178    [0.6359, 0.8022, 0.0],
179    [0.6438, 0.8021, 0.0],
180    [0.6516, 0.8021, 0.0],
181    [0.6593, 0.802, 0.0],
182    [0.667, 0.802, 0.0],
183    [0.6727, 0.8019, 0.0],
184    [0.6802, 0.8019, 0.0],
185    [0.6877, 0.8018, 0.0],
186    [0.6951, 0.8017, 0.0],
187    [0.7024, 0.8016, 0.0],
188    [0.7097, 0.8016, 0.0],
189    [0.717, 0.8015, 0.0],
190    [0.7241, 0.8014, 0.0],
191    [0.7313, 0.8013, 0.0],
192    [0.7383, 0.8012, 0.0],
193    [0.7454, 0.8011, 0.0],
194    [0.7524, 0.8009, 0.0],
195    [0.7576, 0.8009, 0.0],
196    [0.7645, 0.8007, 0.0],
197    [0.7713, 0.8006, 0.0],
198    [0.7781, 0.8005, 0.0],
199    [0.7849, 0.8003, 0.0],
200    [0.7916, 0.8002, 0.0],
201    [0.7983, 0.8, 0.0],
202    [0.806, 0.7936, 0.0],
203    [0.8138, 0.7849, 0.0],
204    [0.8214, 0.7763, 0.0],
205    [0.8287, 0.7677, 0.0],
206    [0.8358, 0.759, 0.0],
207    [0.8409, 0.7525, 0.0],
208    [0.8476, 0.7438, 0.0],
209    [0.8541, 0.7351, 0.0],
210    [0.8603, 0.7264, 0.0],
211    [0.8664, 0.7177, 0.0],
212    [0.8723, 0.7089, 0.0],
213    [0.878, 0.7001, 0.0],
214    [0.8835, 0.6913, 0.0],
215    [0.8888, 0.6824, 0.0],
216    [0.894, 0.6736, 0.0],
217    [0.899, 0.6647, 0.0],
218    [0.9038, 0.6557, 0.0],
219    [0.9074, 0.649, 0.0],
220    [0.9119, 0.64, 0.0],
221    [0.9163, 0.631, 0.0],
222    [0.9206, 0.622, 0.0],
223    [0.9247, 0.6129, 0.0],
224    [0.9287, 0.6037, 0.0],
225    [0.9326, 0.5946, 0.0],
226    [0.9363, 0.5853, 0.0],
227    [0.9399, 0.5761, 0.0],
228    [0.9434, 0.5667, 0.0],
229    [0.9467, 0.5573, 0.0],
230    [0.9499, 0.5479, 0.0],
231    [0.9522, 0.5408, 0.0],
232    [0.9553, 0.5312, 0.0],
233    [0.9582, 0.5216, 0.0],
234    [0.9609, 0.5119, 0.0],
235    [0.9636, 0.5022, 0.0],
236    [0.9662, 0.4923, 0.0],
237    [0.9686, 0.4824, 0.0],
238    [0.971, 0.4723, 0.0],
239    [0.9732, 0.4622, 0.0],
240    [0.9753, 0.4519, 0.0],
241    [0.9774, 0.4416, 0.0],
242    [0.9793, 0.4311, 0.0],
243    [0.9807, 0.4231, 0.0],
244    [0.9825, 0.4124, 0.0],
245    [0.9842, 0.4014, 0.0],
246    [0.9858, 0.3904, 0.0],
247    [0.9873, 0.3791, 0.0],
248    [0.9887, 0.3676, 0.0],
249    [0.99, 0.3559, 0.0],
250    [0.9912, 0.3439, 0.0],
251    [0.9924, 0.3316, 0.0],
252    [0.9934, 0.319, 0.0],
253    [0.9944, 0.306, 0.0],
254    [0.9953, 0.2926, 0.0],
255    [0.9959, 0.2823, 0.0],
256    [0.9967, 0.268, 0.0],
257    [0.9974, 0.2531, 0.0],
258    [0.998, 0.2374, 0.0],
259    [0.9985, 0.2209, 0.0],
260    [0.9989, 0.2031, 0.0],
261    [0.9993, 0.1839, 0.0],
262];
263
264/// Sample the named colormap (default: xenosite). `z` in [0,1] sequential,
265/// or pass `diverging=true` to remap [-1,1] → [0,1] first.
266pub fn sample_colormap(z: f64, diverging: bool) -> [f64; 3] {
267    let t = if diverging { ((z + 1.0) * 0.5).clamp(0.0, 1.0) } else { z.clamp(0.0, 1.0) };
268    let n = XENOSITE.len();
269    if n == 1 { return XENOSITE[0]; }
270    let x = t * (n - 1) as f64;
271    let i = (x as usize).min(n - 2);
272    let f = x - i as f64;
273    let a = XENOSITE[i];
274    let b = XENOSITE[i + 1];
275    [
276        a[0] + (b[0] - a[0]) * f,
277        a[1] + (b[1] - a[1]) * f,
278        a[2] + (b[2] - a[2]) * f,
279    ]
280}
281
282/// CSS `rgb(r,g,b)` for a shade stop.
283pub fn colormap_rgb(z: f64, diverging: bool) -> String {
284    let [r, g, b] = sample_colormap(z, diverging);
285    format!(
286        "rgb({},{},{})",
287        (r * 255.0 + 0.5) as i32,
288        (g * 255.0 + 0.5) as i32,
289        (b * 255.0 + 0.5) as i32
290    )
291}
292
293#[cfg(test)]
294mod tests {
295    use super::*;
296    #[test]
297    fn xenosite_ends() {
298        assert_eq!(sample_colormap(0.0, false), [1.0, 1.0, 1.0]);
299        let hot = sample_colormap(1.0, false);
300        assert!(hot[0] > 0.9 && hot[1] < 0.4 && hot[2] < 0.1);
301    }
302    #[test]
303    fn mid_is_greenish() {
304        let [r, g, b] = sample_colormap(0.5, false);
305        assert!(g > r && g > b);
306    }
307}