Skip to content

Commit 9181b7d

Browse files
authored
Custom colormap trait validation (#99)
* allow setting colormap on init * flake8 fixes * custom colormap trait validation
1 parent e1c081d commit 9181b7d

File tree

4 files changed

+50
-12
lines changed

4 files changed

+50
-12
lines changed

docs/source/api_reference/isocolor.rst

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@ IsoColor
33

44
The ``IsoColor`` widget colorize your mesh given.
55

6-
.. note::
7-
Currently, you can only use the default Viridis colormap, we'd like to support all paraview colormaps
8-
96
The ``input`` attribute should be the name of the ``Component`` you want to use for colorizing the mesh. You also need to pass the ``min`` and ``max`` (or ``range`` as a min/max tuple) of the component array.
107

118
For example, if you have a 1-D ``Data`` named ``"height"``, you can simply pass its name as input:
@@ -73,8 +70,8 @@ Examples
7370
height_min = np.min(z)
7471
height_max = np.max(z)
7572

76-
# Colorize by height
77-
colored_mesh = IsoColor(mesh, input='height', min=height_min, max=height_max)
73+
# Colorize by height and set the colormap to "Turbo"
74+
colored_mesh = IsoColor(mesh, input='height', min=height_min, max=height_max, colormap='Turbo')
7875

7976
# Create a slider that will dynamically change the boundaries of the colormap
8077
colormap_slider_range = FloatRangeSlider(value=[height_min, height_max], min=height_min, max=height_max, step=(height_max - height_min) / 100.)

ipygany/ipygany.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import numpy as np
66

77
from traitlets import (
8-
Bool, Dict, Enum, Unicode, List, Instance, CFloat, Tuple, TraitError, Union, default, validate
8+
Bool, Dict, Enum, Unicode, List, Instance, CFloat, Tuple, TraitError, Union, default, validate, Any,
99
)
1010
from traittypes import Array
1111
from ipywidgets import (
@@ -647,7 +647,7 @@ class IsoColor(Effect):
647647
min = CFloat(0.).tag(sync=True)
648648
max = CFloat(0.).tag(sync=True)
649649
range = Tuple((0., 0.)).tag(sync=True)
650-
colormap = Enum(list(colormaps.values()), allow_none=False, default_value=colormaps.Viridis).tag(sync=True)
650+
colormap = Any(allow_none=False, default_value=colormaps.Viridis).tag(sync=True)
651651
type = Enum(['linear', 'log'], default_value='linear').tag(sync=True)
652652

653653
def __init__(self, parent, **kwargs):
@@ -659,6 +659,31 @@ def input_dim(self):
659659
"""Input dimension."""
660660
return 1
661661

662+
@validate('colormap')
663+
def _valid_colormap(self, proposal):
664+
"""Validate colormap. """
665+
# Logic is here to allow setting ``colormap`` using strings
666+
# and ints. See:
667+
# https://github.com/QuantStack/ipygany/pull/99
668+
value = proposal['value']
669+
670+
# set colormap
671+
if isinstance(value, str):
672+
if value not in colormaps:
673+
allowed = ', '.join([f"'{key}'" for key in colormaps.keys()])
674+
raise ValueError(f'``colormap`` "{value} is not supported by ``ipygany``\n'
675+
'Pick from one of the following:\n' + allowed)
676+
colormap = colormaps[value]
677+
elif isinstance(value, int):
678+
if value not in colormaps.values():
679+
raise ValueError(f'Invalid colormap {value}. Expected one of'
680+
f'the following: {colormaps.values()}')
681+
colormap = value
682+
elif value is not None:
683+
raise TypeError(f'Invalid colormap type {type(value)}. Should be '
684+
'a valid colormap ``str`` or ``int``')
685+
return colormap
686+
662687

663688
class ColorBar(_GanyDOMWidgetBase):
664689
"""A ColorBar widget."""

tests/test_ipygany.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import pytest
2-
31
import numpy as np
42

53
from ipydatawidgets import NDArrayWidget

tests/test_isocolor.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import pytest
22

3-
from traitlets import TraitError
4-
53
from ipygany import PolyMesh, IsoColor
4+
from ipygany.colormaps import colormaps
65

76
from .utils import get_test_assets
87

@@ -12,7 +11,7 @@ def test_default_input():
1211

1312
poly = PolyMesh(vertices=vertices, triangle_indices=triangles, data=[data_1d, data_3d])
1413

15-
colored_mesh = IsoColor(poly)
14+
colored_mesh = IsoColor(poly, colormap='Turbo')
1615

1716
assert colored_mesh.input == '1d'
1817

@@ -23,6 +22,25 @@ def test_default_input():
2322
assert colored_mesh.input == (('3d', 'x'), )
2423

2524

25+
def test_colormaps():
26+
vertices, triangles, data_1d, data_3d = get_test_assets()
27+
28+
poly = PolyMesh(vertices=vertices, triangle_indices=triangles, data=[data_1d, data_3d])
29+
30+
colored_mesh = IsoColor(poly, colormap='Turbo')
31+
assert colored_mesh.colormap == colormaps.Turbo
32+
33+
colormap = colormaps.Cividis
34+
colored_mesh.colormap = colormap
35+
assert colored_mesh.colormap == colormap
36+
37+
with pytest.raises(ValueError):
38+
colored_mesh.colormap = 'InvalidColor'
39+
40+
with pytest.raises(ValueError):
41+
colored_mesh.colormap = -1
42+
43+
2644
def test_input():
2745
vertices, triangles, data_1d, data_3d = get_test_assets()
2846

0 commit comments

Comments
 (0)