From f147b62a0da22e0f0d1be76233f9d9564dce2bb9 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Mon, 30 May 2016 14:26:55 -0400 Subject: [PATCH 1/5] Allow more than 2 colormaps again --- plotly/tools.py | 88 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 31 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 5ffe5eb6f95..33584a1ea53 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1468,9 +1468,9 @@ def _find_intermediate_color(lowcolor, highcolor, intermed): diff_1 = float(highcolor[1] - lowcolor[1]) diff_2 = float(highcolor[2] - lowcolor[2]) - inter_colors = np.array([lowcolor[0] + intermed * diff_0, - lowcolor[1] + intermed * diff_1, - lowcolor[2] + intermed * diff_2]) + inter_colors = (lowcolor[0] + intermed * diff_0, + lowcolor[1] + intermed * diff_1, + lowcolor[2] + intermed * diff_2) return inter_colors @staticmethod @@ -1503,14 +1503,14 @@ def _unconvert_from_RGB_255(colors): return un_rgb_colors @staticmethod - def _map_array2color(array, colormap, vmin, vmax): + def _map_face2color(face, colormap, vmin, vmax): """ - Normalize values in array by vmin/vmax and return plotly color strings. + Normalize facecolor values by vmin/vmax and return rgb-color strings - This function takes an array of values along with a colormap and a - minimum (vmin) and maximum (vmax) range of possible z values for the - given parametrized surface. It returns an rgb color based on the - relative position of zval between vmin and vmax + This function takes a tuple color with elements between 0 and 1, along + with a colormap and a minimum (vmin) and maximum (vmax) range of + possible mean distances for the given parametrized surface. It returns + an rgb color based on the mean distance between vmin and vmax """ if vmin >= vmax: @@ -1518,20 +1518,38 @@ def _map_array2color(array, colormap, vmin, vmax): "and vmax. The vmin value cannot be " "bigger than or equal to the value " "of vmax.") - # find distance t of zval from vmin to vmax where the distance - # is normalized to be between 0 and 1 - t = (array - vmin) / float((vmax - vmin)) - t_colors = FigureFactory._find_intermediate_color(colormap[0], - colormap[1], - t) - t_colors = t_colors * 255. - labelled_colors = ['rgb(%s, %s, %s)' % (i, j, k) - for i, j, k in t_colors.T] - return labelled_colors + # find the normalized distance t of a triangle face between vmin and + #vmax where the distance is normalized between 0 and 1 + t = (face - vmin) / float((vmax - vmin)) + + if len(colormap) <= 1: + t_color = colormap[0] + color = FigureFactory._convert_to_RGB_255(t_color) + color = FigureFactory._label_rgb(color) + else: + # account for colormaps with more than one color + incr = 1./(len(colormap) - 1) + low_color_index = int(t/incr) + + if t == 1: + t_color = colormap[low_color_index] + color = FigureFactory._convert_to_RGB_255(t_color) + color = FigureFactory._label_rgb(color) + + else: + t_color = FigureFactory._find_intermediate_color( + colormap[low_color_index], + colormap[low_color_index + 1], + (t - low_color_index * incr) / incr) + color = FigureFactory._convert_to_RGB_255(t_color) + color = FigureFactory._label_rgb(color) + + return color @staticmethod def _trisurf(x, y, z, simplices, colormap=None, color_func=None, - plot_edges=False, x_edge=None, y_edge=None, z_edge=None): + plot_edges=False, x_edge=None, y_edge=None, z_edge=None, + facecolor=None): """ Refer to FigureFactory.create_trisurf() for docstring """ @@ -1556,11 +1574,11 @@ def _trisurf(x, y, z, simplices, colormap=None, color_func=None, if len(color_func) != len(simplices): raise ValueError("If color_func is a list/array, it must " "be the same length as simplices.") - # convert all colors to rgb - for index in range(len(color_func)): - if '#' in color_func[index]: - foo = FigureFactory._hex_to_rgb(color_func[index]) - color_func[index] = FigureFactory._label_rgb(foo) + # convert all colors to rgb + for index in range(len(color_func)): + if '#' in color_func[index]: + foo = FigureFactory._hex_to_rgb(color_func[index]) + color_func[index] = FigureFactory._label_rgb(foo) mean_dists = np.asarray(color_func) else: @@ -1581,10 +1599,16 @@ def _trisurf(x, y, z, simplices, colormap=None, color_func=None, else: min_mean_dists = np.min(mean_dists) max_mean_dists = np.max(mean_dists) - facecolor = FigureFactory._map_array2color(mean_dists, - colormap, - min_mean_dists, - max_mean_dists) + + if facecolor is None: + facecolor = [] + for index in range(len(mean_dists)): + color = FigureFactory._map_face2color(mean_dists[index], + colormap, + min_mean_dists, + max_mean_dists) + facecolor.append(color) + # Make sure we have arrays to speed up plotting facecolor = np.asarray(facecolor) ii, jj, kk = simplices.T @@ -2931,11 +2955,13 @@ def _label_rgb(colors): """ if isinstance(colors, tuple): - return 'rgb{}'.format(colors) + return ('rgb(%s, %s, %s)' % (colors[0], colors[1], colors[2])) else: colors_label = [] for color in colors: - color_label = 'rgb{}'.format(color) + color_label = ('rgb(%s, %s, %s)' % (color[0], + color[1], + color[2])) colors_label.append(color_label) return colors_label From d1709b09fbfc59c54bf94a878931c66f8a6017f5 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Mon, 30 May 2016 14:55:43 -0400 Subject: [PATCH 2/5] Fixed test error and updated json schema --- plotly/graph_reference/default-schema.json | 80 +++++++++++++++++++ .../test_optional/test_figure_factory.py | 3 +- plotly/tools.py | 8 +- 3 files changed, 87 insertions(+), 4 deletions(-) diff --git a/plotly/graph_reference/default-schema.json b/plotly/graph_reference/default-schema.json index cc0ae722eec..27cdf82abf6 100644 --- a/plotly/graph_reference/default-schema.json +++ b/plotly/graph_reference/default-schema.json @@ -11230,6 +11230,7 @@ }, "lighting": { "ambient": { + "description": "Ambient light increases overall color visibility but can wash out the image.", "dflt": 0.8, "max": 1, "min": 0, @@ -11237,13 +11238,23 @@ "valType": "number" }, "diffuse": { + "description": "Represents the extent that incident rays are reflected in a range of angles.", "dflt": 0.8, "max": 1, "min": 0, "role": "style", "valType": "number" }, + "facenormalsepsilon": { + "description": "Epsilon for face normals calculation avoids math issues arising from degenerate geometry.", + "dflt": 1e-06, + "max": 1, + "min": 0, + "role": "style", + "valType": "number" + }, "fresnel": { + "description": "Represents the reflectance as a dependency of the viewing angle; e.g. paper is reflective when viewing it from the edge of the paper (almost 90 degrees), causing shine.", "dflt": 0.2, "max": 5, "min": 0, @@ -11252,6 +11263,7 @@ }, "role": "object", "roughness": { + "description": "Alters specular reflection; the rougher the surface, the wider and less contrasty the shine.", "dflt": 0.5, "max": 1, "min": 0, @@ -11259,11 +11271,47 @@ "valType": "number" }, "specular": { + "description": "Represents the level that incident rays are reflected in a single direction, causing shine.", "dflt": 0.05, "max": 2, "min": 0, "role": "style", "valType": "number" + }, + "vertexnormalsepsilon": { + "description": "Epsilon for vertex normals calculation avoids math issues arising from degenerate geometry.", + "dflt": 1e-12, + "max": 1, + "min": 0, + "role": "style", + "valType": "number" + } + }, + "lightposition": { + "role": "object", + "x": { + "description": "Numeric vector, representing the X coordinate for each vertex.", + "dflt": 100000, + "max": 100000, + "min": -100000, + "role": "style", + "valType": "number" + }, + "y": { + "description": "Numeric vector, representing the Y coordinate for each vertex.", + "dflt": 100000, + "max": 100000, + "min": -100000, + "role": "style", + "valType": "number" + }, + "z": { + "description": "Numeric vector, representing the Z coordinate for each vertex.", + "dflt": 0, + "max": 100000, + "min": -100000, + "role": "style", + "valType": "number" } }, "name": { @@ -17937,6 +17985,7 @@ }, "lighting": { "ambient": { + "description": "Ambient light increases overall color visibility but can wash out the image.", "dflt": 0.8, "max": 1, "min": 0, @@ -17944,6 +17993,7 @@ "valType": "number" }, "diffuse": { + "description": "Represents the extent that incident rays are reflected in a range of angles.", "dflt": 0.8, "max": 1, "min": 0, @@ -17951,6 +18001,7 @@ "valType": "number" }, "fresnel": { + "description": "Represents the reflectance as a dependency of the viewing angle; e.g. paper is reflective when viewing it from the edge of the paper (almost 90 degrees), causing shine.", "dflt": 0.2, "max": 5, "min": 0, @@ -17959,6 +18010,7 @@ }, "role": "object", "roughness": { + "description": "Alters specular reflection; the rougher the surface, the wider and less contrasty the shine.", "dflt": 0.5, "max": 1, "min": 0, @@ -17966,6 +18018,7 @@ "valType": "number" }, "specular": { + "description": "Represents the level that incident rays are reflected in a single direction, causing shine.", "dflt": 0.05, "max": 2, "min": 0, @@ -17973,6 +18026,33 @@ "valType": "number" } }, + "lightposition": { + "role": "object", + "x": { + "description": "Numeric vector, representing the X coordinate for each vertex.", + "dflt": 10, + "max": 100000, + "min": -100000, + "role": "style", + "valType": "number" + }, + "y": { + "description": "Numeric vector, representing the Y coordinate for each vertex.", + "dflt": 100000, + "max": 100000, + "min": -100000, + "role": "style", + "valType": "number" + }, + "z": { + "description": "Numeric vector, representing the Z coordinate for each vertex.", + "dflt": 0, + "max": 100000, + "min": -100000, + "role": "style", + "valType": "number" + } + }, "name": { "description": "Sets the trace name. The trace name appear as the legend item and on hover.", "role": "info", diff --git a/plotly/tests/test_optional/test_figure_factory.py b/plotly/tests/test_optional/test_figure_factory.py index 8604595c987..608320338e6 100644 --- a/plotly/tests/test_optional/test_figure_factory.py +++ b/plotly/tests/test_optional/test_figure_factory.py @@ -929,7 +929,8 @@ def test_trisurf_all_args(self): z, simplices, color_func=colors_bad) # Check converting custom colors to strings test_colors_plot = tls.FigureFactory.create_trisurf( - x, y, z, simplices, color_func=colors_raw) + x, y, z, simplices, color_func=colors_raw + ) self.assertTrue(isinstance(test_colors_plot['data'][0]['facecolor'][0], str)) diff --git a/plotly/tools.py b/plotly/tools.py index 33584a1ea53..ecb3535c4ff 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1574,11 +1574,13 @@ def _trisurf(x, y, z, simplices, colormap=None, color_func=None, if len(color_func) != len(simplices): raise ValueError("If color_func is a list/array, it must " "be the same length as simplices.") + # convert all colors to rgb for index in range(len(color_func)): - if '#' in color_func[index]: - foo = FigureFactory._hex_to_rgb(color_func[index]) - color_func[index] = FigureFactory._label_rgb(foo) + if isinstance(color_func[index], str): + if '#' in color_func[index]: + foo = FigureFactory._hex_to_rgb(color_func[index]) + color_func[index] = FigureFactory._label_rgb(foo) mean_dists = np.asarray(color_func) else: From c5339c1b9e8c3b896c6a70f1c3241630df67aba9 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Mon, 30 May 2016 15:25:31 -0400 Subject: [PATCH 3/5] Updated doc strings --- plotly/tools.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index ecb3535c4ff..3409a5ea536 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1507,10 +1507,10 @@ def _map_face2color(face, colormap, vmin, vmax): """ Normalize facecolor values by vmin/vmax and return rgb-color strings - This function takes a tuple color with elements between 0 and 1, along - with a colormap and a minimum (vmin) and maximum (vmax) range of - possible mean distances for the given parametrized surface. It returns - an rgb color based on the mean distance between vmin and vmax + This function takes a tuple color along with a colormap and a minimum + (vmin) and maximum (vmax) range of possible mean distances for the + given parametrized surface. It returns an rgb color based on the mean + distance between vmin and vmax """ if vmin >= vmax: @@ -1679,10 +1679,12 @@ def create_trisurf(x, y, z, simplices, colormap=None, color_func=None, :param (array) simplices: an array of shape (ntri, 3) where ntri is the number of triangles in the triangularization. Each row of the array contains the indicies of the verticies of each triangle - :param (str|list) colormap: either a plotly scale name, or a list - containing 2 triplets. These triplets must be of the form (a,b,c) - or 'rgb(x,y,z)' where a,b,c belong to the interval [0,1] and x,y,z - belong to [0,255] + :param (str|tuple|list) colormap: either a plotly scale name, an rgb + or hex color, a color tuple or a list of colors. An rgb color is + of the form 'rgb(x, y, z)' where x, y, z belong to the interval + [0, 255] and a color tuple is a tuple of the form (a, b, c) where + a, b and c belong to [0, 1]. If colormap is a list, it must + contain the valid color types aforementioned as its members. :param (function|list) color_func: The parameter that determines the coloring of the surface. Takes either a function with 3 arguments x, y, z or a list/array of color values the same length as From c89cda10a5dc66d7d9bc783c8e55e78953d20b71 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Thu, 2 Jun 2016 09:44:02 -0400 Subject: [PATCH 4/5] Added legibility to mapface2color func et PEP8 --- plotly/tools.py | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 3409a5ea536..1a3df867c31 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1518,33 +1518,32 @@ def _map_face2color(face, colormap, vmin, vmax): "and vmax. The vmin value cannot be " "bigger than or equal to the value " "of vmax.") - # find the normalized distance t of a triangle face between vmin and - #vmax where the distance is normalized between 0 and 1 - t = (face - vmin) / float((vmax - vmin)) - - if len(colormap) <= 1: - t_color = colormap[0] - color = FigureFactory._convert_to_RGB_255(t_color) - color = FigureFactory._label_rgb(color) + + if len(colormap) == 1: + # color each triangle face with the same color in colormap + face_color = colormap[0] + face_color = FigureFactory._convert_to_RGB_255(face_color) + face_color = FigureFactory._label_rgb(face_color) else: - # account for colormaps with more than one color - incr = 1./(len(colormap) - 1) - low_color_index = int(t/incr) + # find the normalized distance t of a triangle face between vmin + # and vmax where the distance is normalized between 0 and 1 + t = (face - vmin) / float((vmax - vmin)) + low_color_index = int(t / (1./(len(colormap) - 1))) if t == 1: - t_color = colormap[low_color_index] - color = FigureFactory._convert_to_RGB_255(t_color) - color = FigureFactory._label_rgb(color) + face_color = colormap[low_color_index] + face_color = FigureFactory._convert_to_RGB_255(face_color) + face_color = FigureFactory._label_rgb(face_color) else: - t_color = FigureFactory._find_intermediate_color( + face_color = FigureFactory._find_intermediate_color( colormap[low_color_index], colormap[low_color_index + 1], - (t - low_color_index * incr) / incr) - color = FigureFactory._convert_to_RGB_255(t_color) - color = FigureFactory._label_rgb(color) + t * (len(colormap) - 1) - low_color_index) + face_color = FigureFactory._convert_to_RGB_255(face_color) + face_color = FigureFactory._label_rgb(face_color) - return color + return face_color @staticmethod def _trisurf(x, y, z, simplices, colormap=None, color_func=None, From dda0a9a09779b0844f67b88c9d6a44fea6f497d0 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Fri, 3 Jun 2016 11:38:38 -0400 Subject: [PATCH 5/5] Made facecoloring neater --- plotly/tools.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 1a3df867c31..c2cf7a394e1 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1525,17 +1525,17 @@ def _map_face2color(face, colormap, vmin, vmax): face_color = FigureFactory._convert_to_RGB_255(face_color) face_color = FigureFactory._label_rgb(face_color) else: - # find the normalized distance t of a triangle face between vmin - # and vmax where the distance is normalized between 0 and 1 - t = (face - vmin) / float((vmax - vmin)) - low_color_index = int(t / (1./(len(colormap) - 1))) - - if t == 1: - face_color = colormap[low_color_index] + if face == vmax: + # pick last color in colormap + face_color = colormap[-1] face_color = FigureFactory._convert_to_RGB_255(face_color) face_color = FigureFactory._label_rgb(face_color) - else: + # find the normalized distance t of a triangle face between + # vmin and vmax where the distance is between 0 and 1 + t = (face - vmin) / float((vmax - vmin)) + low_color_index = int(t / (1./(len(colormap) - 1))) + face_color = FigureFactory._find_intermediate_color( colormap[low_color_index], colormap[low_color_index + 1],