Skip to content

Commit fbe8d2a

Browse files
committed
Creating color blending function to speed up trisurf color calculation
1 parent a1c0a9c commit fbe8d2a

File tree

1 file changed

+71
-49
lines changed

1 file changed

+71
-49
lines changed

plotly/tools.py

Lines changed: 71 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3022,47 +3022,60 @@ def _unconvert_from_RGB_255(colors):
30223022
return un_rgb_color
30233023

30243024
@staticmethod
3025-
def _map_face2color(face, colormap, vmin, vmax):
3025+
def _map_faces2color(faces, colormap):
30263026
"""
3027-
Normalize facecolor values by vmin/vmax and return rgb-color strings
3028-
3029-
This function takes a tuple color along with a colormap and a minimum
3030-
(vmin) and maximum (vmax) range of possible mean distances for the
3031-
given parametrized surface. It returns an rgb color based on the mean
3032-
distance between vmin and vmax
3027+
Normalize facecolors by their min/max and return rgb-color strings.
30333028
3029+
This function takes a tuple color along with a colormap.
3030+
It returns an rgb color based on the mean distance between the
3031+
minimum and maximum value of faces.
30343032
"""
3035-
if vmin >= vmax:
3036-
raise exceptions.PlotlyError("Incorrect relation between vmin "
3037-
"and vmax. The vmin value cannot be "
3038-
"bigger than or equal to the value "
3039-
"of vmax.")
3040-
3041-
if len(colormap) == 1:
3033+
colormap = np.atleast_2d(colormap)
3034+
if colormap.shape[0] == 1:
30423035
# color each triangle face with the same color in colormap
3043-
face_color = colormap[0]
3044-
face_color = FigureFactory._convert_to_RGB_255(face_color)
3045-
face_color = FigureFactory._label_rgb(face_color)
3036+
face_colors = colormap
30463037
else:
3047-
if face == vmax:
3048-
# pick last color in colormap
3049-
face_color = colormap[-1]
3050-
face_color = FigureFactory._convert_to_RGB_255(face_color)
3051-
face_color = FigureFactory._label_rgb(face_color)
3052-
else:
3053-
# find the normalized distance t of a triangle face between
3054-
# vmin and vmax where the distance is between 0 and 1
3055-
t = (face - vmin) / float((vmax - vmin))
3056-
low_color_index = int(t / (1./(len(colormap) - 1)))
3057-
3058-
face_color = FigureFactory._find_intermediate_color(
3059-
colormap[low_color_index],
3060-
colormap[low_color_index + 1],
3061-
t * (len(colormap) - 1) - low_color_index)
3062-
face_color = FigureFactory._convert_to_RGB_255(face_color)
3063-
face_color = FigureFactory._label_rgb(face_color)
3038+
# Convert face values to between 0 and 1
3039+
vmin = faces.min()
3040+
vmax = faces.max()
3041+
if vmin >= vmax:
3042+
raise exceptions.PlotlyError("Incorrect relation between vmin"
3043+
" and vmax. The vmin value cannot"
3044+
" be bigger than or equal to the"
3045+
" value of vmax.")
3046+
# Scale t to between 0 and 1
3047+
t = (faces - vmin) / float((vmax - vmin))
3048+
t_ixs = np.round(t * 255).astype(int)
3049+
3050+
# If a list of colors is given, interpolate between them.
3051+
color_range = FigureFactory._blend_colors(colormap)
3052+
face_colors = color_range[t_ixs]
3053+
3054+
# Convert to 255 scale, and round to nearest integer
3055+
face_colors = np.round(face_colors * 255., 0)
3056+
face_colors = FigureFactory._label_rgb(face_colors)
3057+
return face_colors
30643058

3065-
return face_color
3059+
@staticmethod
3060+
def _blend_colors(colormap, n_colors=255.):
3061+
if len(colormap) == 1:
3062+
raise ValueError('Cannot blend a colormap with only one color')
3063+
# Figure out how many splits we need
3064+
n_split = np.floor(n_colors / (len(colormap) - 1)).astype(int)
3065+
n_remain = np.mod(n_colors, len(colormap))
3066+
3067+
# Iterate through pairs of colors
3068+
color_range = []
3069+
for ii in range(len(colormap) - 1):
3070+
# For each channel (r, g, b)
3071+
this_interp = []
3072+
for cstt, cstp in zip(colormap[ii], colormap[ii + 1]):
3073+
# If it's not an even split, add req'd amount on first iter
3074+
n_interp = n_split + n_remain if ii == 0 else n_split
3075+
this_interp.append(np.linspace(cstt, cstp, n_interp))
3076+
color_range.append(np.vstack(this_interp).T)
3077+
color_range = np.vstack(color_range)
3078+
return color_range
30663079

30673080
@staticmethod
30683081
def _trisurf(x, y, z, simplices, colormap=None, color_func=None,
@@ -3117,17 +3130,12 @@ def _trisurf(x, y, z, simplices, colormap=None, color_func=None,
31173130
if isinstance(mean_dists[0], str):
31183131
facecolor = mean_dists
31193132
else:
3120-
min_mean_dists = np.min(mean_dists)
3121-
max_mean_dists = np.max(mean_dists)
3122-
3123-
if facecolor is None:
3124-
facecolor = []
3125-
for index in range(len(mean_dists)):
3126-
color = FigureFactory._map_face2color(mean_dists[index],
3127-
colormap,
3128-
min_mean_dists,
3129-
max_mean_dists)
3130-
facecolor.append(color)
3133+
# Map distances to color using the given cmap
3134+
dist_colors = FigureFactory._map_faces2color(mean_dists, colormap)
3135+
if facecolor is not None:
3136+
facecolor = np.vstack([facecolor, dist_colors])
3137+
else:
3138+
facecolor = dist_colors
31313139

31323140
# Make sure we have arrays to speed up plotting
31333141
facecolor = np.asarray(facecolor)
@@ -4338,8 +4346,17 @@ def _convert_to_RGB_255(colors):
43384346
"""
43394347
Multiplies each element of a triplet by 255
43404348
"""
4341-
4342-
return (colors[0]*255.0, colors[1]*255.0, colors[2]*255.0)
4349+
if isinstance(colors, tuple):
4350+
return (colors[0]*255.0, colors[1]*255.0, colors[2]*255.0)
4351+
elif isinstance(colors, np.ndarray):
4352+
# Vectorize the multiplication and return a list of tuples
4353+
return [tuple(ii) for ii in colors * 255.0]
4354+
else:
4355+
colors_255 = []
4356+
for color in colors:
4357+
rgb_color = (color[0]*255.0, color[1]*255.0, color[2]*255.0)
4358+
colors_255.append(rgb_color)
4359+
return colors_255
43434360

43444361
@staticmethod
43454362
def _n_colors(lowcolor, highcolor, n_colors):
@@ -4372,7 +4389,12 @@ def _label_rgb(colors):
43724389
"""
43734390
Takes tuple (a, b, c) and returns an rgb color 'rgb(a, b, c)'
43744391
"""
4375-
return ('rgb(%s, %s, %s)' % (colors[0], colors[1], colors[2]))
4392+
if isinstance(colors, tuple):
4393+
return ('rgb(%s, %s, %s)' % (colors[0], colors[1], colors[2]))
4394+
else:
4395+
colors_label = ['rgb(%s, %s, %s)' % (r, g, b)
4396+
for r, g, b in colors]
4397+
return colors_label
43764398

43774399
@staticmethod
43784400
def _unlabel_rgb(colors):

0 commit comments

Comments
 (0)