Skip to content

Commit 1266151

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

File tree

1 file changed

+67
-43
lines changed

1 file changed

+67
-43
lines changed

plotly/tools.py

Lines changed: 67 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3022,7 +3022,7 @@ 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
"""
30273027
Normalize facecolor values by vmin/vmax and return rgb-color strings
30283028
@@ -3032,37 +3032,52 @@ def _map_face2color(face, colormap, vmin, vmax):
30323032
distance between vmin and vmax
30333033
30343034
"""
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:
3035+
colormap = np.atleast_2d(colormap)
3036+
if colormap.shape[0] == 1:
30423037
# 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)
3038+
face_colors = colormap
30463039
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)))
3040+
# Convert face values to between 0 and 1
3041+
vmin = faces.min()
3042+
vmax = faces.max()
3043+
if vmin >= vmax:
3044+
raise exceptions.PlotlyError("Incorrect relation between vmin"
3045+
" and vmax. The vmin value cannot"
3046+
" be bigger than or equal to the"
3047+
" value of vmax.")
3048+
# Scale t to between 0 and 1
3049+
t = (faces - vmin) / float((vmax - vmin))
3050+
t_ixs = np.round(t * 255).astype(int)
3051+
3052+
# If a list of colors is given, interpolate between them.
3053+
color_range = FigureFactory._blend_colors(colormap)
3054+
face_colors = color_range[t_ixs]
3055+
3056+
# Convert to 255 scale, and round to nearest integer
3057+
face_colors = np.round(face_colors * 255., 0)
3058+
face_colors = FigureFactory._label_rgb(face_colors)
3059+
return face_colors
30573060

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

30673082
@staticmethod
30683083
def _trisurf(x, y, z, simplices, colormap=None, color_func=None,
@@ -3117,17 +3132,12 @@ def _trisurf(x, y, z, simplices, colormap=None, color_func=None,
31173132
if isinstance(mean_dists[0], str):
31183133
facecolor = mean_dists
31193134
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)
3135+
# Map distances to color using the given cmap
3136+
dist_colors = FigureFactory._map_faces2color(mean_dists, colormap)
3137+
if facecolor is not None:
3138+
facecolor = np.vstack([facecolor, dist_colors])
3139+
else:
3140+
facecolor = dist_colors
31313141

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

43444363
@staticmethod
43454364
def _n_colors(lowcolor, highcolor, n_colors):
@@ -4372,7 +4391,12 @@ def _label_rgb(colors):
43724391
"""
43734392
Takes tuple (a, b, c) and returns an rgb color 'rgb(a, b, c)'
43744393
"""
4375-
return ('rgb(%s, %s, %s)' % (colors[0], colors[1], colors[2]))
4394+
if isinstance(colors, tuple):
4395+
return ('rgb(%s, %s, %s)' % (colors[0], colors[1], colors[2]))
4396+
else:
4397+
colors_label = ['rgb(%s, %s, %s)' % (r, g, b)
4398+
for r, g, b in colors]
4399+
return colors_label
43764400

43774401
@staticmethod
43784402
def _unlabel_rgb(colors):

0 commit comments

Comments
 (0)