Skip to content

Allow more than 2 colormaps again #493

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 3, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions plotly/graph_reference/default-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -11230,20 +11230,31 @@
},
"lighting": {
"ambient": {
"description": "Ambient light increases overall color visibility but can wash out the image.",
"dflt": 0.8,
"max": 1,
"min": 0,
"role": "style",
"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,
Expand All @@ -11252,18 +11263,55 @@
},
"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,
"role": "style",
"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": {
Expand Down Expand Up @@ -17937,20 +17985,23 @@
},
"lighting": {
"ambient": {
"description": "Ambient light increases overall color visibility but can wash out the image.",
"dflt": 0.8,
"max": 1,
"min": 0,
"role": "style",
"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"
},
"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,
Expand All @@ -17959,20 +18010,49 @@
},
"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,
"role": "style",
"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"
}
},
"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",
Expand Down
3 changes: 2 additions & 1 deletion plotly/tests/test_optional/test_figure_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
93 changes: 61 additions & 32 deletions plotly/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the benefit gained by making it a list and looping through? IME using arrays instead of lists almost always makes things much faster.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the benefit gained by making it a list and looping through? IME using arrays instead of lists almost always makes things much faster.

I don't necessarily think it's faster or anything like that. I actually just switched it back to this because I wanted it to work with my FF converting code

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if there's a benefit other than speed. E.g., if you thought the user might pass in tuples of variable shapes then it's probably better for the input to be a list or tuple or something (since arrays require consistency across dimensions). If you like, maybe I can take a pass through and make it work w/ arrays similar to the last PR. I don't think the syntax would be any different, and it'll likely be an order of magnitude faster to use arrays.

lowcolor[1] + intermed * diff_1,
lowcolor[2] + intermed * diff_2)
return inter_colors

@staticmethod
Expand Down Expand Up @@ -1503,35 +1503,52 @@ 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):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is support for multiple colormaps mentioned in the docstring for trisurf anywhere? (or is it supported in a different function that calls this function?) I didn't see any mention of multiple colormaps. What's the usecase for including multiple colormaps in the call to trisurf, e.g., how would people specify which colormap goes with which datapoint?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Kully and @choldgraf did you guys resolve this one?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I see now what @Kully was getting at, though it took a fair bit of digging for me to figure it out. I wanted to ask - in terms of interpolating between points of a list of multiple colormaps, why not use colorlover? You could just do cl.interp(list_of_colors, 10000)[int(t * 10000)] right?

"""
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 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:
raise exceptions.PlotlyError("Incorrect relation between vmin "
"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

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:
if face == vmax:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is even clearer than what I was suggesting. thanks :)

# pick last color in colormap
face_color = colormap[-1]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 🔔 🎆

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],
t * (len(colormap) - 1) - low_color_index)
face_color = FigureFactory._convert_to_RGB_255(face_color)
face_color = FigureFactory._label_rgb(face_color)

return face_color
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💥 so much more readable! thanks!


@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
"""
Expand All @@ -1556,8 +1573,10 @@ 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)):

# convert all colors to rgb
for index in range(len(color_func)):
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)
Expand All @@ -1581,10 +1600,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
Expand Down Expand Up @@ -1653,10 +1678,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
Expand Down Expand Up @@ -2931,11 +2958,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
Expand Down