testing/polishing

This commit is contained in:
Martin Diehl 2020-06-27 19:43:35 +02:00
parent cf63226721
commit c929af12c0
2 changed files with 119 additions and 27 deletions

View File

@ -17,7 +17,7 @@ class Colormap(mpl.colors.ListedColormap):
@staticmethod @staticmethod
def from_bounds(low,high,N=256,name='DAMASK colormap',model='rgb'): def from_bounds(low,high,name='DAMASK colormap',N=256,model='rgb'):
""" """
Create a perceptually uniform colormap. Create a perceptually uniform colormap.
@ -26,30 +26,46 @@ class Colormap(mpl.colors.ListedColormap):
low : numpy.ndarray of shape (3) low : numpy.ndarray of shape (3)
high : numpy.ndarray of shape (3) high : numpy.ndarray of shape (3)
N : integer, optional N : integer, optional
Number of discrete color values. Defaults to 256. The number of color quantization levels.
name : str, optional
The name of the colormap. Defaults to `DAMASK colormap`.
model : str model : str
Colormodel used for low and high. Colormodel used for low and high.
""" """
low_,high_ = map(np.array,[low,high]) low_,high_ = map(np.array,[low,high])
low_high = np.vstack((low_,high))
if model.lower() == 'rgb': if model.lower() == 'rgb':
# ToDo: Sanity check if np.any(low_high<0) or np.any(low_high>1):
raise ValueError(f'RGB color out of range {low} {high}.')
low_,high_ = map(Colormap._rgb2msh,[low_,high_]) low_,high_ = map(Colormap._rgb2msh,[low_,high_])
elif model.lower() == 'hsv': elif model.lower() == 'hsv':
# ToDo: Sanity check if np.any(low_high<0) or np.any(low_high[:,1:3]>1) or np.any(low_high[:,0]>360):
raise ValueError(f'HSV color out of range {low} {high}.')
low_,high_ = map(Colormap._hsv2msh,[low_,high_]) low_,high_ = map(Colormap._hsv2msh,[low_,high_])
elif model.lower() == 'hsl': elif model.lower() == 'hsl':
# ToDo: Sanity check if np.any(low_high<0) or np.any(low_high[:,1:3]>1) or np.any(low_high[:,0]>360):
raise ValueError(f'HSL color out of range {low} {high}.')
low_,high_ = map(Colormap._hsl2msh,[low_,high_]) low_,high_ = map(Colormap._hsl2msh,[low_,high_])
elif model.lower() == 'xyz': elif model.lower() == 'xyz':
# ToDo: Sanity check
low_,high_ = map(Colormap._xyz2msh,[low_,high_]) low_,high_ = map(Colormap._xyz2msh,[low_,high_])
elif model.lower() == 'lab': elif model.lower() == 'lab':
# ToDo: Sanity check if np.any(low_high[:,0]<0):
raise ValueError(f'CIE Lab color out of range {low} {high}.')
low_,high_ = map(Colormap._lab2msh,[low_,high_]) low_,high_ = map(Colormap._lab2msh,[low_,high_])
elif model.lower() == 'msh': elif model.lower() == 'msh':
# ToDo: Sanity check
pass pass
else: else:
raise ValueError(f'Invalid color model: {model}.') raise ValueError(f'Invalid color model: {model}.')
@ -64,14 +80,15 @@ class Colormap(mpl.colors.ListedColormap):
""" """
Select from set of predefined colormaps. Select from set of predefined colormaps.
Predefined colormaps include matplotlib-native colormaps Predefined colormaps include native matplotlib colormaps
and common DAMASK colormaps. and common DAMASK colormaps.
Parameters Parameters
---------- ----------
name : str name : str
The name of the colormap.
N : int, optional N : int, optional
Number of discrete color values. Defaults to 256. The number of color quantization levels. Defaults to 256.
This parameter is not used for matplotlib colormaps This parameter is not used for matplotlib colormaps
that are of type `ListedColormap`. that are of type `ListedColormap`.
@ -84,11 +101,11 @@ class Colormap(mpl.colors.ListedColormap):
if isinstance(colormap,mpl.colors.LinearSegmentedColormap): if isinstance(colormap,mpl.colors.LinearSegmentedColormap):
return Colormap(np.array(list(map(colormap,np.linspace(0,1,N)))),name=name) return Colormap(np.array(list(map(colormap,np.linspace(0,1,N)))),name=name)
else: else:
return Colormap(colormap.colors,name=name) return Colormap(np.array(colormap.colors),name=name)
# DAMASK presets # DAMASK presets
definition = Colormap._predefined_DAMASK[name] definition = Colormap._predefined_DAMASK[name]
return Colormap.from_bounds(definition['left'],definition['right'],N,name) return Colormap.from_bounds(definition['left'],definition['right'],name,N)
@staticmethod @staticmethod
@ -102,6 +119,7 @@ class Colormap(mpl.colors.ListedColormap):
def show(self): def show(self):
"""Show colormap in window."""
fig, ax = plt.subplots(figsize=(10,1)) fig, ax = plt.subplots(figsize=(10,1))
ax.set_axis_off() ax.set_axis_off()
im = ax.imshow(np.broadcast_to(np.linspace(0,1,640).reshape(1,-1),(64,640)),cmap=self) # noqa im = ax.imshow(np.broadcast_to(np.linspace(0,1,640).reshape(1,-1),(64,640)),cmap=self) # noqa
@ -109,6 +127,26 @@ class Colormap(mpl.colors.ListedColormap):
plt.show() plt.show()
def reversed(self,name=None):
"""
Make a reversed instance of the Colormap.
Parameters
----------
name : str, optional
The name for the reversed colormap. If it's None
the name will be the name of the parent colormap + "_r".
Returns
-------
damask.Colormap
The reversed colormap.
"""
rev = super(Colormap,self).reversed(name)
return Colormap(rev.colors,rev.name)
def to_file(self,fname=None,format='paraview'): def to_file(self,fname=None,format='paraview'):
if fname is not None: if fname is not None:
try: try:
@ -122,15 +160,16 @@ class Colormap(mpl.colors.ListedColormap):
Colormap._export_paraview(self,f) Colormap._export_paraview(self,f)
elif format.lower() == 'ascii': elif format.lower() == 'ascii':
Colormap._export_ASCII(self,f) Colormap._export_ASCII(self,f)
elif format.lower() == 'gom':
def reversed(self): Colormap._export_GOM(self,f)
rev = super(Colormap,self).reversed() elif format.lower() == 'gmsh':
return Colormap(rev.colors,rev.name) Colormap._export_gmsh(self,f)
else:
raise ValueError('Unknown output format: {format}.')
@staticmethod @staticmethod
def _export_paraview(colormap,fhandle=None): def _export_paraview(colormap,fhandle=None):
"""Write colormap to JSON file for Paraview."""
colors = [] colors = []
for i,c in enumerate(np.round(colormap.colors,6).tolist()): for i,c in enumerate(np.round(colormap.colors,6).tolist()):
colors+=[i]+c colors+=[i]+c
@ -149,10 +188,11 @@ class Colormap(mpl.colors.ListedColormap):
@staticmethod @staticmethod
def _export_ASCII(colormap,fhandle=None): def _export_ASCII(colormap,fhandle=None):
"""Write colormap to ASCII table."""
labels = {'R':(1,),'G':(1,),'B':(1,)} labels = {'R':(1,),'G':(1,),'B':(1,)}
if colormap.colors.shape[1] == 4: labels['alpha']=(1,) if colormap.colors.shape[1] == 4: labels['alpha']=(1,)
t = Table(colormap.colors,labels) t = Table(colormap.colors,labels)
if fhandle is None: if fhandle is None:
with open(colormap.name.replace(' ','_')+'.txt', 'w') as f: with open(colormap.name.replace(' ','_')+'.txt', 'w') as f:
t.to_ASCII(f) t.to_ASCII(f)
@ -162,17 +202,18 @@ class Colormap(mpl.colors.ListedColormap):
@staticmethod @staticmethod
def _export_GOM(colormap,fhandle=None): def _export_GOM(colormap,fhandle=None):
pass pass
# a = f'1 1 {name.replace(" ","_"} 9 {name.replace(" ","_"} ' s =(f'1 1 {colormap.name.replace(" ","_")} 9 {colormap.name.replace(" ","_")} '
# f' 0 1 0 3 0 0 -1 9 \\ 0 0 0 255 255 255 0 0 255 ' f' 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 ' + str(len(colors)) f'30 NO_UNIT 1 1 64 64 64 255 1 0 0 0 0 0 0 3 0 {str(len(colormap.colors))}'
# f' '.join([' 0 %s 255 1'%(' '.join([str(int(x*255.0)) for x in color])) for color in reversed(colors)])] ' '.join([' 0 %s 255 1'%(' '.join([str(int(x*255.0)) for x in color])) for color in reversed(colormap.colors)]))
print(s)
@staticmethod @staticmethod
def _export_gmsh(colormap,fname=None): def _export_gmsh(colormap,fname=None):
colors = colormap.colors colors = colormap.colors
colormap = ['View.ColorTable = {'] \ colormap =('View.ColorTable = {'
+ [',\n'.join(['{%s}'%(','.join([str(x*255.0) for x in color])) for color in colors])] \ ',\n'.join(['{%s}'%(','.join([str(x*255.0) for x in color])) for color in colors])+\
+ ['}'] '}')
@staticmethod @staticmethod
def _interpolate_msh(frac,low, high): def _interpolate_msh(frac,low, high):

View File

@ -1,4 +1,7 @@
import os
import numpy as np import numpy as np
import pytest
from damask import Colormap from damask import Colormap
@ -15,7 +18,7 @@ class TestColormap:
[1.,0.,1.], [1.,0.,1.],
[1.,1.,1.] [1.,1.,1.]
]) ])
rgbs = np.vstack((specials,np.random.rand(10000,3))) rgbs = np.vstack((specials,np.random.rand(100,3)))
pass # class not integrated pass # class not integrated
for rgb in rgbs: for rgb in rgbs:
print('rgb',rgb) print('rgb',rgb)
@ -59,3 +62,51 @@ class TestColormap:
# xyz2msh # xyz2msh
assert np.allclose(Colormap._xyz2msh(xyz),msh,atol=1.e-6,rtol=0) assert np.allclose(Colormap._xyz2msh(xyz),msh,atol=1.e-6,rtol=0)
@pytest.mark.parametrize('format',['ASCII','paraview','GOM','gmsh'])
@pytest.mark.parametrize('model',['rgb','hsv','hsl','xyz','lab','msh'])
def test_from_bounds(self,model,format,tmpdir):
N = np.random.randint(2,256)
c = Colormap.from_bounds(np.random.rand(3),np.random.rand(3),model=model,N=N)
c.to_file(tmpdir/'color_out',format=format)
@pytest.mark.parametrize('format',['ASCII','paraview','GOM','gmsh'])
@pytest.mark.parametrize('name',['strain','gnuplot','Greys','PRGn','viridis'])
def test_from_predefined(self,name,format,tmpdir):
N = np.random.randint(2,256)
c = Colormap.from_predefined(name,N)
os.chdir(tmpdir)
c.to_file(format=format)
@pytest.mark.parametrize('format,name',[('ASCII','test.txt'),
('paraview','test.json'),
('GOM','test.legend'),
('gmsh','test.xxx')
])
def test_write_filehandle(self,format,name,tmpdir):
c = Colormap.from_predefined('Dark2')
with open(tmpdir/name,'w') as f:
c.to_file(f,format=format)
def test_invalid_format(self):
c = Colormap.from_predefined('Dark2')
with pytest.raises(ValueError):
c.to_file(format='invalid')
@pytest.mark.parametrize('model',['rgb','hsv','hsl','lab','invalid'])
def test_invalid_color(self,model):
with pytest.raises(ValueError):
c = Colormap.from_bounds(-2.+np.random.rand(3),np.random.rand(3),N=10,model=model) # noqa
def test_reversed(self):
c_1 = Colormap.from_predefined('stress')
c_2 = c_1.reversed()
assert (not np.allclose(c_1.colors,c_2.colors)) and \
np.allclose(c_1.colors,c_2.reversed().colors)
def test_list(self):
c = Colormap.from_predefined('afmhot').reversed()
c.list_predefined()