From 947bf946e1dfae6f278d61513cc4b7e092987ed7 Mon Sep 17 00:00:00 2001 From: Eureka Pai Date: Fri, 10 Dec 2021 11:31:26 -0500 Subject: [PATCH 1/5] added Colormap.at(fraction) to interpolate --- python/damask/_colormap.py | 28 +++++++++++++++++++++++++--- python/tests/test_Colormap.py | 7 +++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index 233be569a..36bcf45c0 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -6,6 +6,7 @@ from pathlib import Path from typing import Sequence, Union, TextIO import numpy as np +import scipy.interpolate as interp import matplotlib as mpl if os.name == 'posix' and 'DISPLAY' not in os.environ: mpl.use('Agg') @@ -191,6 +192,28 @@ class Colormap(mpl.colors.ListedColormap): return Colormap.from_range(definition['low'],definition['high'],name,N) + def at(self, + fraction : float) -> np.ndarray: + """ + Interpolate color at fraction. + + Parameters + ---------- + fraction : float + Fractional coordinate to evaluate Colormap at. + + Returns + ------- + color : np.ndarray, shape(3) + RGB values of interpolated color. + + """ + return interp.interp1d(np.linspace(0,1,self.N), + self.colors, + axis=0, + assume_sorted=True)(fraction) + + def shade(self, field: np.ndarray, bounds: Sequence[float] = None, @@ -213,7 +236,6 @@ class Colormap(mpl.colors.ListedColormap): RGBA image of shaded data. """ - N = len(self.colors) mask = np.logical_not(np.isnan(field) if gap is None else \ np.logical_or (np.isnan(field), field == gap)) # mask NaN (and gap if present) @@ -227,7 +249,7 @@ class Colormap(mpl.colors.ListedColormap): return Image.fromarray( (np.dstack(( - self.colors[(np.round(np.clip((field-lo)/(hi-lo),0.0,1.0)*(N-1))).astype(np.uint16),:3], + self.colors[(np.round(np.clip((field-lo)/(hi-lo),0.0,1.0)*(self.N-1))).astype(np.uint16),:3], mask.astype(float) ) )*255 @@ -343,7 +365,7 @@ class Colormap(mpl.colors.ListedColormap): # ToDo: test in GOM GOM_str = '1 1 {name} 9 {name} '.format(name=self.name.replace(" ","_")) \ + '0 1 0 3 0 0 -1 9 \\ 0 0 0 255 255 255 0 0 255 ' \ - + f'30 NO_UNIT 1 1 64 64 64 255 1 0 0 0 0 0 0 3 0 {len(self.colors)}' \ + + f'30 NO_UNIT 1 1 64 64 64 255 1 0 0 0 0 0 0 3 0 {self.N}' \ + ' '.join([f' 0 {c[0]} {c[1]} {c[2]} 255 1' for c in reversed((self.colors*255).astype(int))]) \ + '\n' diff --git a/python/tests/test_Colormap.py b/python/tests/test_Colormap.py index ab9bcf92f..2c2c78560 100644 --- a/python/tests/test_Colormap.py +++ b/python/tests/test_Colormap.py @@ -139,6 +139,13 @@ class TestColormap: c += c assert (np.allclose(c.colors[:len(c.colors)//2],c.colors[len(c.colors)//2:])) + @pytest.mark.parametrize('N,cmap',[ + (8,'gray'), + (17,'gray'), + ]) + def test_at(self, N, cmap): + assert np.allclose(Colormap.from_predefined(cmap,N=N).at(0.5)[:3],0.5,rtol=0.005) + @pytest.mark.parametrize('bounds',[None,[2,10]]) def test_shade(self,ref_path,update,bounds): data = np.add(*np.indices((10, 11))) From 7bb6707b0e8af00f44a97cc4d7d9a2012b71ead9 Mon Sep 17 00:00:00 2001 From: Franz Roters Date: Fri, 10 Dec 2021 18:07:04 +0100 Subject: [PATCH 2/5] [skip ci] Web page was moved --- python/damask/_colormap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index 36bcf45c0..6b8da4909 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -42,7 +42,7 @@ class Colormap(mpl.colors.ListedColormap): https://doi.org/10.1016/j.ijplas.2012.09.012 Matplotlib colormaps overview - https://matplotlib.org/tutorials/colors/colormaps.html + https://matplotlib.org/stable/tutorials/colors/colormaps.html """ From 2b6283bbe301048b140a966deed708e4e964c78f Mon Sep 17 00:00:00 2001 From: Eureka Pai Date: Fri, 10 Dec 2021 12:52:44 -0500 Subject: [PATCH 3/5] flexibility to return with alpha channel and/or as string --- python/damask/_colormap.py | 32 +++++++++++++++++++++++--------- python/tests/test_Colormap.py | 23 +++++++++++++++++------ 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index 6b8da4909..97330baf3 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -193,25 +193,39 @@ class Colormap(mpl.colors.ListedColormap): def at(self, - fraction : float) -> np.ndarray: + fraction : Union[float,Sequence[float]], + with_alpha : bool = False, + as_string : bool = False) -> np.ndarray: """ Interpolate color at fraction. Parameters ---------- - fraction : float - Fractional coordinate to evaluate Colormap at. + fraction : float or sequence of float + Fractional coordinate(s) to evaluate Colormap at. + with_alpha : bool + Provide opacity as fourth value. + as_string : bool + Encode color as 'rgb(r,g,b)' or 'rgba(r,g,b,a)'. Returns ------- - color : np.ndarray, shape(3) - RGB values of interpolated color. + color : np.ndarray, shape(3 or 4), or string + RGB(A) values of interpolated color. """ - return interp.interp1d(np.linspace(0,1,self.N), - self.colors, - axis=0, - assume_sorted=True)(fraction) + def _stringify(color): + d = color.shape[-1] + c = np.array([f'rgba({c[0]},{c[1]},{c[2]},{c[3]})' if d==4 + else f'rgb({c[0]},{c[1]},{c[2]})' for c in np.atleast_2d(color)]) + return c if len(c)>1 else c[0] + + + color = interp.interp1d(np.linspace(0,1,self.N), + self.colors, + axis=0, + assume_sorted=True)(fraction)[...,:4 if with_alpha else 3] + return _stringify(color) if as_string else color def shade(self, diff --git a/python/tests/test_Colormap.py b/python/tests/test_Colormap.py index 2c2c78560..d411ca39d 100644 --- a/python/tests/test_Colormap.py +++ b/python/tests/test_Colormap.py @@ -139,12 +139,23 @@ class TestColormap: c += c assert (np.allclose(c.colors[:len(c.colors)//2],c.colors[len(c.colors)//2:])) - @pytest.mark.parametrize('N,cmap',[ - (8,'gray'), - (17,'gray'), - ]) - def test_at(self, N, cmap): - assert np.allclose(Colormap.from_predefined(cmap,N=N).at(0.5)[:3],0.5,rtol=0.005) + @pytest.mark.parametrize('N,cmap,at,with_alpha,result',[ + (8,'gray',0.5,False,[0.5,0.5,0.5]), + (17,'gray',0.5,False,[0.5,0.5,0.5]), + (17,'gray',[0.5,0.75],False,[[0.5,0.5,0.5],[0.75,0.75,0.75]]), + ]) + def test_at_value(self, N, cmap, at, with_alpha, result): + assert np.allclose(Colormap.from_predefined(cmap,N=N).at(at,with_alpha=with_alpha), + result, + rtol=0.005) + + @pytest.mark.parametrize('N,cmap,at,with_alpha,result',[ + (8,'gray',0.5,False,'rgb(0.5,0.5,0.5)'), + (8,'gray',0.5,True,'rgba(0.5,0.5,0.5,1.0)'), + (8,'gray',[0.5,0.25],True,['rgba(0.5,0.5,0.5,1.0)','rgba(0.25,0.25,0.25,1.0)']), + ]) + def test_at_string(self, N, cmap, at, with_alpha, result): + assert np.all(Colormap.from_predefined(cmap,N=N).at(at,with_alpha=with_alpha,as_string=True) == result) @pytest.mark.parametrize('bounds',[None,[2,10]]) def test_shade(self,ref_path,update,bounds): From 32595299c9e779d2c321a81d34c18f15858472af Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Mon, 13 Dec 2021 17:44:24 -0500 Subject: [PATCH 4/5] minimal clean API for Colormap.at --- python/damask/_colormap.py | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index 97330baf3..5a13f440d 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -193,9 +193,7 @@ class Colormap(mpl.colors.ListedColormap): def at(self, - fraction : Union[float,Sequence[float]], - with_alpha : bool = False, - as_string : bool = False) -> np.ndarray: + fraction : Union[float,Sequence[float]]) -> np.ndarray: """ Interpolate color at fraction. @@ -203,29 +201,26 @@ class Colormap(mpl.colors.ListedColormap): ---------- fraction : float or sequence of float Fractional coordinate(s) to evaluate Colormap at. - with_alpha : bool - Provide opacity as fourth value. - as_string : bool - Encode color as 'rgb(r,g,b)' or 'rgba(r,g,b,a)'. Returns ------- - color : np.ndarray, shape(3 or 4), or string - RGB(A) values of interpolated color. + color : np.ndarray, shape(...,4) + RGBA values of interpolated color(s). + + Examples + -------- + >>> import damask + >>> cmap = damask.Colormap.from_predefined('gray') + >>> cmap.at(0.5) + array([0.5, 0.5, 0.5, 1. ]) + >>> 'rgb({},{},{})'.format(*cmap.at(0.5)) + 'rgb(0.5,0.5,0.5)' """ - def _stringify(color): - d = color.shape[-1] - c = np.array([f'rgba({c[0]},{c[1]},{c[2]},{c[3]})' if d==4 - else f'rgb({c[0]},{c[1]},{c[2]})' for c in np.atleast_2d(color)]) - return c if len(c)>1 else c[0] - - - color = interp.interp1d(np.linspace(0,1,self.N), - self.colors, - axis=0, - assume_sorted=True)(fraction)[...,:4 if with_alpha else 3] - return _stringify(color) if as_string else color + return interp.interp1d(np.linspace(0,1,self.N), + self.colors, + axis=0, + assume_sorted=True)(fraction) def shade(self, From 2b4487f42667c6572d6f86b56cac10690ce718da Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Mon, 13 Dec 2021 19:07:18 -0500 Subject: [PATCH 5/5] forgot to update test --- python/tests/test_Colormap.py | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/python/tests/test_Colormap.py b/python/tests/test_Colormap.py index d411ca39d..156907670 100644 --- a/python/tests/test_Colormap.py +++ b/python/tests/test_Colormap.py @@ -139,24 +139,16 @@ class TestColormap: c += c assert (np.allclose(c.colors[:len(c.colors)//2],c.colors[len(c.colors)//2:])) - @pytest.mark.parametrize('N,cmap,at,with_alpha,result',[ - (8,'gray',0.5,False,[0.5,0.5,0.5]), - (17,'gray',0.5,False,[0.5,0.5,0.5]), - (17,'gray',[0.5,0.75],False,[[0.5,0.5,0.5],[0.75,0.75,0.75]]), + @pytest.mark.parametrize('N,cmap,at,result',[ + (8,'gray',0.5,[0.5,0.5,0.5]), + (17,'gray',0.5,[0.5,0.5,0.5]), + (17,'gray',[0.5,0.75],[[0.5,0.5,0.5],[0.75,0.75,0.75]]), ]) - def test_at_value(self, N, cmap, at, with_alpha, result): - assert np.allclose(Colormap.from_predefined(cmap,N=N).at(at,with_alpha=with_alpha), + def test_at_value(self, N, cmap, at, result): + assert np.allclose(Colormap.from_predefined(cmap,N=N).at(at)[...,:3], result, rtol=0.005) - @pytest.mark.parametrize('N,cmap,at,with_alpha,result',[ - (8,'gray',0.5,False,'rgb(0.5,0.5,0.5)'), - (8,'gray',0.5,True,'rgba(0.5,0.5,0.5,1.0)'), - (8,'gray',[0.5,0.25],True,['rgba(0.5,0.5,0.5,1.0)','rgba(0.25,0.25,0.25,1.0)']), - ]) - def test_at_string(self, N, cmap, at, with_alpha, result): - assert np.all(Colormap.from_predefined(cmap,N=N).at(at,with_alpha=with_alpha,as_string=True) == result) - @pytest.mark.parametrize('bounds',[None,[2,10]]) def test_shade(self,ref_path,update,bounds): data = np.add(*np.indices((10, 11)))