|
| 1 | +import os |
| 2 | +from typing import Callable |
| 3 | + |
| 4 | +import matplotlib.colors as pltc |
| 5 | +import matplotlib.pyplot as plt |
| 6 | +import numpy as np |
| 7 | + |
| 8 | +from plotpy.mathutils.colormap import ( |
| 9 | + DEFAULT_COLORMAPS, |
| 10 | + DEFAULT_COLORMAPS_PATH, |
| 11 | + EditableColormap, |
| 12 | + save_colormaps, |
| 13 | +) |
| 14 | + |
| 15 | + |
| 16 | +def rgb_colors_to_hex_list(colors: list[tuple[int, int, int]]): |
| 17 | + return [(i / len(colors), pltc.to_hex(color)) for i, color in enumerate(colors)] |
| 18 | + |
| 19 | + |
| 20 | +def _interpolate(val, vmin, vmax): |
| 21 | + """Interpolate a color component between to values as provided |
| 22 | + by matplotlib colormaps |
| 23 | + """ |
| 24 | + interp = (val - vmin[0]) / (vmax[0] - vmin[0]) |
| 25 | + return (1 - interp) * vmin[1] + interp * vmax[2] |
| 26 | + |
| 27 | + |
| 28 | +def std_segmented_cmap_to_hex_list(cmdata: dict[str, list[tuple[float, float, float]]]): |
| 29 | + """Setup a CustomQwtLinearColorMap according to |
| 30 | + matplotlib's data |
| 31 | + """ |
| 32 | + colors: list[tuple[float, str]] = [] |
| 33 | + red = np.array(cmdata["red"]) |
| 34 | + green = np.array(cmdata["green"]) |
| 35 | + blue = np.array(cmdata["blue"]) |
| 36 | + indices = sorted(set(red[:, 0]) | set(green[:, 0]) | set(blue[:, 0])) |
| 37 | + for i in indices: |
| 38 | + idxr = red[:, 0].searchsorted(i) |
| 39 | + idxg = green[:, 0].searchsorted(i) |
| 40 | + idxb = blue[:, 0].searchsorted(i) |
| 41 | + compr = _interpolate(i, red[idxr - 1], red[idxr]) |
| 42 | + compg = _interpolate(i, green[idxg - 1], green[idxg]) |
| 43 | + compb = _interpolate(i, blue[idxb - 1], blue[idxb]) |
| 44 | + colors.append((i, pltc.to_hex((compr, compg, compb)))) |
| 45 | + return colors |
| 46 | + |
| 47 | + |
| 48 | +InterpFuncT = Callable[[np.ndarray], np.ndarray] |
| 49 | + |
| 50 | + |
| 51 | +def func_segmented_cmap_to_hex_list( |
| 52 | + n: int, |
| 53 | + cmap: pltc.LinearSegmentedColormap, |
| 54 | +): |
| 55 | + colors = [] |
| 56 | + arr = np.linspace(0, 1, n, dtype=float) |
| 57 | + colors = [(i, pltc.to_hex(rgba)) for i, rgba in zip(arr, cmap(arr))] |
| 58 | + return colors |
| 59 | + |
| 60 | + |
| 61 | +def interp_to_descrete_cmap(cmap: EditableColormap) -> EditableColormap: |
| 62 | + raw_cmap: tuple[tuple[float, str], ...] = cmap.to_tuples() |
| 63 | + new_raw_cmap: list[tuple[float, str]] = [raw_cmap[0]] |
| 64 | + n = len(raw_cmap) |
| 65 | + coeff = (n - 1) / n |
| 66 | + for i, (pos, color) in enumerate(raw_cmap[1:]): |
| 67 | + prev_pos, prev_color = raw_cmap[i] |
| 68 | + curr_pos, curr_color = pos, color |
| 69 | + new_pos = curr_pos * coeff |
| 70 | + print(curr_pos, new_pos, coeff) |
| 71 | + new_raw_cmap.append((new_pos, prev_color)) |
| 72 | + new_raw_cmap.append((new_pos, curr_color)) |
| 73 | + new_raw_cmap.append(raw_cmap[-1]) |
| 74 | + |
| 75 | + return EditableColormap.from_iterable(new_raw_cmap, name=cmap.name) |
| 76 | + |
| 77 | + |
| 78 | +def main(): |
| 79 | + |
| 80 | + new_cmaps: dict[str, list[tuple[float, str]]] = {} |
| 81 | + |
| 82 | + cmaps_with_colors = [ |
| 83 | + "magma", |
| 84 | + "viridis", |
| 85 | + "inferno", |
| 86 | + "plasma", |
| 87 | + "cividis", |
| 88 | + ] |
| 89 | + |
| 90 | + descrete_cmaps = [ |
| 91 | + "Pastel1", |
| 92 | + "Pastel2", |
| 93 | + "Paired", |
| 94 | + "Accent", |
| 95 | + "Dark2", |
| 96 | + "Set1", |
| 97 | + "Set2", |
| 98 | + "Set3", |
| 99 | + ] |
| 100 | + |
| 101 | + cmaps_with_colors.extend(descrete_cmaps) |
| 102 | + |
| 103 | + for cm_name in cmaps_with_colors: |
| 104 | + cmap = plt.get_cmap(cm_name) |
| 105 | + new_cmaps[cm_name] = rgb_colors_to_hex_list(cmap.colors) |
| 106 | + |
| 107 | + for cm_name in descrete_cmaps: |
| 108 | + cmap = EditableColormap.from_iterable(new_cmaps[cm_name], name=cm_name) |
| 109 | + new_cmaps[cm_name] = list(interp_to_descrete_cmap(cmap).to_tuples()) |
| 110 | + |
| 111 | + segmented_cmaps = [ |
| 112 | + "coolwarm", |
| 113 | + "bwr", |
| 114 | + "seismic", |
| 115 | + ] |
| 116 | + |
| 117 | + for cm_name in segmented_cmaps: |
| 118 | + cmap = plt.get_cmap(cm_name) |
| 119 | + new_cmaps[cm_name] = std_segmented_cmap_to_hex_list(cmap._segmentdata) |
| 120 | + |
| 121 | + n = 128 |
| 122 | + interp_cmaps = ["gnuplot2", "CMRmap", "rainbow", "turbo", "afmhot"] |
| 123 | + |
| 124 | + for cm_name in interp_cmaps: |
| 125 | + cmap = plt.get_cmap(cm_name) |
| 126 | + new_cmaps[cm_name] = func_segmented_cmap_to_hex_list(n, cmap) |
| 127 | + |
| 128 | + for name, raw_cm in new_cmaps.items(): |
| 129 | + DEFAULT_COLORMAPS[name.lower()] = EditableColormap.from_iterable( |
| 130 | + raw_cm, name=name |
| 131 | + ) |
| 132 | + |
| 133 | + json_file = os.path.join(os.path.dirname(__file__), "new_colormaps.json") |
| 134 | + save_colormaps(json_file, DEFAULT_COLORMAPS_PATH) |
| 135 | + |
| 136 | + |
| 137 | +if __name__ == "__main__": |
| 138 | + main() |
0 commit comments