diff --git a/brainspace/plotting/colormaps.py b/brainspace/plotting/colormaps.py index cb8af5b..b3d7a50 100644 --- a/brainspace/plotting/colormaps.py +++ b/brainspace/plotting/colormaps.py @@ -9,4 +9,57 @@ [70, 130, 180, 255], [196, 58, 250, 255]], dtype=np.uint8) -colormaps = {'yeo7': yeo7_colors} +eco_kos_colors = np.array([[0, 0, 0, 255], + [126, 40, 127, 255], + [51, 104, 156, 255], + [167, 210, 140, 255], + [254, 205, 8, 255], + [255, 253, 25, 255]], dtype=np.uint8) + +# 5-step Spectral palette (control points from ColorBrewer 2.0, Apache 2.0). +# Reference: https://colorbrewer2.org +spec_5_colors = np.array([[0, 0, 0, 255], + [50, 136, 189, 255], + [171, 221, 164, 255], + [235, 235, 181, 255], + [253, 174, 97, 255], + [213, 62, 79, 255]], dtype=np.uint8) + + +def _interp_lut(stops, n=256): + """Build a 256-row RGBA uint8 lookup by linearly interpolating ``stops``. + + ``stops`` is an (k, 3) or (k, 4) array of RGB(A) values in [0, 255]. + Output always has alpha = 255 unless explicit alphas are provided. + """ + stops = np.asarray(stops, dtype=np.float64) + k = stops.shape[0] + xs = np.linspace(0.0, 1.0, k) + grid = np.linspace(0.0, 1.0, n) + cols = stops.shape[1] + out = np.empty((n, 4), dtype=np.float64) + for c in range(min(cols, 4)): + out[:, c] = np.interp(grid, xs, stops[:, c]) + if cols < 4: + out[:, 3] = 255.0 + return np.clip(out, 0, 255).astype(np.uint8) + + +# Diverging blue->grey->red colormap. Built programmatically from 5 control +# points to keep the file small and reviewable instead of hand-listing 256 +# RGB tuples. +_BuGyRd_stops = np.array([ + [33, 113, 181], # blue + [120, 170, 210], # light blue + [200, 200, 200], # grey midpoint + [220, 90, 60], # warm red + [103, 0, 13], # dark red +]) +BuGyRd = _interp_lut(_BuGyRd_stops, n=256) + +colormaps = { + 'yeo7': yeo7_colors, + 'eco_kos': eco_kos_colors, + 'spec_5': spec_5_colors, + 'BuGyRd': BuGyRd, +} diff --git a/brainspace/plotting/surface_plotting.py b/brainspace/plotting/surface_plotting.py index 0f23069..fc74233 100644 --- a/brainspace/plotting/surface_plotting.py +++ b/brainspace/plotting/surface_plotting.py @@ -24,7 +24,13 @@ orientations = {'medial': (0, -90, -90), 'lateral': (0, 90, 90), 'ventral': (0, 180, 0), - 'dorsal': (0, 0, 0)} + 'dorsal': (0, 0, 0), + 'anterior': (90, 90, 90), + 'posterior': (270, 90, 90), + # 'flatL'/'flatR' assume an already-flattened surface; the + # camera angle alone does not flatten a folded mesh. + 'flatL': (0, 180, 90), + 'flatR': (0, 180, 270)} def _add_colorbar(ren, lut, location, **cb_kwds): diff --git a/brainspace/tests/test_colormaps.py b/brainspace/tests/test_colormaps.py new file mode 100644 index 0000000..e80e904 --- /dev/null +++ b/brainspace/tests/test_colormaps.py @@ -0,0 +1,31 @@ +"""Tests for plotting.colormaps registrations and BuGyRd interpolation (#91).""" + +import numpy as np + +from brainspace.plotting.colormaps import ( + colormaps, yeo7_colors, eco_kos_colors, spec_5_colors, BuGyRd, +) + + +def test_registered_names(): + for name in ('yeo7', 'eco_kos', 'spec_5', 'BuGyRd'): + assert name in colormaps + cm = colormaps[name] + assert cm.dtype == np.uint8 + assert cm.shape[1] == 4 + + +def test_BuGyRd_is_256_smooth_lut(): + assert BuGyRd.shape == (256, 4) + assert np.all(BuGyRd[:, 3] == 255) + # Endpoints should match the configured stops (within rounding). + np.testing.assert_array_equal(BuGyRd[0, :3], [33, 113, 181]) + np.testing.assert_array_equal(BuGyRd[-1, :3], [103, 0, 13]) + # Smoothness: no large per-row jump in any channel. + diffs = np.abs(np.diff(BuGyRd[:, :3].astype(int), axis=0)) + assert diffs.max() < 8 + + +def test_categorical_palettes_have_alpha_255(): + for cm in (yeo7_colors, eco_kos_colors, spec_5_colors): + assert np.all(cm[:, 3] == 255)