33from __future__ import absolute_import
44from __future__ import division
55import pythreejs
6+ from typing import List , Union
7+
68
79__all__ = [
810 'current' ,
2931 'animation_control' ,
3032 'gcc' ,
3133 'transfer_function' ,
34+ 'transfer_function_discrete' ,
3235 'plot_isosurface' ,
3336 'volshow' ,
3437 'save' ,
@@ -894,6 +897,48 @@ def gcc():
894897 return current .container
895898
896899
900+ def transfer_function_discrete (
901+ n ,
902+ colors : List [str ] = ["red" , "green" , "blue" ],
903+ labels : Union [None , List [str ]] = None ,
904+ opacity : Union [float , List [float ]] = 0.1 ,
905+ enabled : Union [bool , List [bool ]] = True ,
906+ controls = True ,
907+ ):
908+ """Create a discrete transfer function with n layers.
909+
910+ Each (integer) value of the volumetric data maps to a single color.
911+
912+ :param n: number of layers
913+ :param colors: list of colors, can be any valid HTML color string
914+ :param labels: list of labels, if None, labels will be "Layer 0", "Layer 1", etc.
915+ :param opacity: opacity of each layer, can be a single value or a list of values
916+ :param enabled: whether each layer is enabled, can be a single value or a list of values
917+ :param controls: whether to add the controls to the current container
918+
919+ """
920+ if isinstance (opacity , float ):
921+ opacity = [opacity ] * len (colors )
922+ if isinstance (enabled , bool ):
923+ enabled = [enabled ] * len (colors )
924+
925+ def ensure_length (x ):
926+ repeat = (n + len (colors ) - 1 ) // len (colors )
927+ return (x * repeat )[:n ]
928+
929+ if labels is None :
930+ labels = []
931+ for i in range (n ):
932+ labels .append (f"Layer { i } " )
933+
934+ tf = ipv .TransferFunctionDiscrete (colors = ensure_length (colors ), opacities = ensure_length (opacity ), enabled = ensure_length (enabled ), labels = ensure_length (labels ))
935+ gcf () # make sure a current container/figure exists
936+ if controls :
937+ current .container .children = [tf .control ()] + current .container .children
938+
939+ return tf
940+
941+
897942def transfer_function (
898943 level = [0.1 , 0.5 , 0.9 ], opacity = [0.01 , 0.05 , 0.1 ], level_width = 0.1 , controls = True , max_opacity = 0.2
899944):
@@ -1029,8 +1074,7 @@ def volshow(
10291074):
10301075 """Visualize a 3d array using volume rendering.
10311076
1032- Currently only 1 volume can be rendered.
1033-
1077+ If the data is of type int8 or bool, :any:`a discrete transfer function will be used <ipv.discrete_transfer_function>`
10341078
10351079 :param data: 3d numpy array
10361080 :param origin: origin of the volume data, this is to match meshes which have a different origin
@@ -1040,7 +1084,7 @@ def volshow(
10401084 :param float data_max: maximum value to consider for data, if None, computed using np.nanmax
10411085 :parap int max_shape: maximum shape for the 3d cube, if larger, the data is reduced by skipping/slicing (data[::N]),
10421086 set to None to disable.
1043- :param tf: transfer function (or a default one)
1087+ :param tf: transfer function (or a default one, based on the data )
10441088 :param bool stereo: stereo view for virtual reality (cardboard and similar VR head mount)
10451089 :param ambient_coefficient: lighting parameter
10461090 :param diffuse_coefficient: lighting parameter
@@ -1060,12 +1104,18 @@ def volshow(
10601104 """
10611105 fig = gcf ()
10621106
1063- if tf is None :
1064- tf = transfer_function (level , opacity , level_width , controls = controls , max_opacity = max_opacity )
10651107 if data_min is None :
10661108 data_min = np .nanmin (data )
10671109 if data_max is None :
10681110 data_max = np .nanmax (data )
1111+ if tf is None :
1112+ if (data .dtype == np .uint8 ) or (data .dtype == bool ):
1113+ if data .dtype == bool :
1114+ data_max = 1
1115+
1116+ tf = transfer_function_discrete (n = data_max + 1 )
1117+ else :
1118+ tf = transfer_function (level , opacity , level_width , controls = controls , max_opacity = max_opacity )
10691119 if memorder == 'F' :
10701120 data = data .T
10711121
0 commit comments