Skip to content

Commit bef62ef

Browse files
committed
Merge pull request #486 from plotly/choldgraf-custom_colors_trisurf
Choldgraf custom colors trisurf
2 parents cf48f62 + 1ae5545 commit bef62ef

File tree

2 files changed

+53
-21
lines changed

2 files changed

+53
-21
lines changed

plotly/tests/test_optional/test_figure_factory.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,26 @@ def test_trisurf_all_args(self):
913913
self.assert_dict_equal(test_trisurf_plot['data'][1],
914914
exp_trisurf_plot['data'][1])
915915

916+
# Test passing custom colors
917+
colors_raw = np.random.randn(simplices.shape[0])
918+
colors_str = ['rgb(%s, %s, %s)' % (i, j, k)
919+
for i, j, k in np.random.randn(simplices.shape[0], 3)]
920+
921+
# Color == strings should be kept the same
922+
test_colors_plot = tls.FigureFactory.create_trisurf(
923+
x, y, z, simplices, color_func=colors_str)
924+
self.assertListEqual(list(test_colors_plot['data'][0]['facecolor']),
925+
list(colors_str))
926+
# Colors must match length of simplices
927+
colors_bad = colors_str[:-1]
928+
self.assertRaises(ValueError, tls.FigureFactory.create_trisurf, x, y,
929+
z, simplices, color_func=colors_bad)
930+
# Check converting custom colors to strings
931+
test_colors_plot = tls.FigureFactory.create_trisurf(
932+
x, y, z, simplices, color_func=colors_raw)
933+
self.assertTrue(isinstance(test_colors_plot['data'][0]['facecolor'][0],
934+
str))
935+
916936

917937
class TestScatterPlotMatrix(NumpyTestUtilsMixin, TestCase):
918938

plotly/tools.py

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,11 +1503,11 @@ def _unconvert_from_RGB_255(colors):
15031503
return un_rgb_colors
15041504

15051505
@staticmethod
1506-
def _map_z2color(zvals, colormap, vmin, vmax):
1506+
def _map_array2color(array, colormap, vmin, vmax):
15071507
"""
1508-
Returns the color corresponding zval's place between vmin and vmax
1508+
Normalize values in array by vmin/vmax and return plotly color strings.
15091509
1510-
This function takes a z value (zval) along with a colormap and a
1510+
This function takes an array of values along with a colormap and a
15111511
minimum (vmin) and maximum (vmax) range of possible z values for the
15121512
given parametrized surface. It returns an rgb color based on the
15131513
relative position of zval between vmin and vmax
@@ -1520,7 +1520,7 @@ def _map_z2color(zvals, colormap, vmin, vmax):
15201520
"of vmax.")
15211521
# find distance t of zval from vmin to vmax where the distance
15221522
# is normalized to be between 0 and 1
1523-
t = (zvals - vmin) / float((vmax - vmin))
1523+
t = (array - vmin) / float((vmax - vmin))
15241524
t_colors = FigureFactory._find_intermediate_color(colormap[0],
15251525
colormap[1],
15261526
t)
@@ -1542,34 +1542,46 @@ def _trisurf(x, y, z, simplices, colormap=None, color_func=None,
15421542
import numpy as np
15431543
from plotly.graph_objs import graph_objs
15441544
points3D = np.vstack((x, y, z)).T
1545+
simplices = np.atleast_2d(simplices)
15451546

15461547
# vertices of the surface triangles
15471548
tri_vertices = points3D[simplices]
15481549

1549-
if not color_func:
1550+
# Define colors for the triangle faces
1551+
if color_func is None:
15501552
# mean values of z-coordinates of triangle vertices
15511553
mean_dists = tri_vertices[:, :, 2].mean(-1)
1554+
elif isinstance(color_func, (list, np.ndarray)):
1555+
# Pre-computed list / array of values to map onto color
1556+
if len(color_func) != len(simplices):
1557+
raise ValueError("If color_func is a list/array, it must "
1558+
"be the same length as simplices.")
1559+
mean_dists = np.asarray(color_func)
15521560
else:
15531561
# apply user inputted function to calculate
15541562
# custom coloring for triangle vertices
15551563
mean_dists = []
1556-
15571564
for triangle in tri_vertices:
15581565
dists = []
15591566
for vertex in triangle:
15601567
dist = color_func(vertex[0], vertex[1], vertex[2])
15611568
dists.append(dist)
1562-
15631569
mean_dists.append(np.mean(dists))
1570+
mean_dists = np.asarray(mean_dists)
15641571

1565-
min_mean_dists = np.min(mean_dists)
1566-
max_mean_dists = np.max(mean_dists)
1567-
facecolor = FigureFactory._map_z2color(mean_dists,
1568-
colormap,
1569-
min_mean_dists,
1570-
max_mean_dists)
1571-
1572-
ii, jj, kk = zip(*simplices)
1572+
# Check if facecolors are already strings and can be skipped
1573+
if isinstance(mean_dists[0], str):
1574+
facecolor = mean_dists
1575+
else:
1576+
min_mean_dists = np.min(mean_dists)
1577+
max_mean_dists = np.max(mean_dists)
1578+
facecolor = FigureFactory._map_array2color(mean_dists,
1579+
colormap,
1580+
min_mean_dists,
1581+
max_mean_dists)
1582+
# Make sure we have arrays to speed up plotting
1583+
facecolor = np.asarray(facecolor)
1584+
ii, jj, kk = simplices.T
15731585
triangles = graph_objs.Mesh3d(x=x, y=y, z=z, facecolor=facecolor,
15741586
i=ii, j=jj, k=kk, name='')
15751587

@@ -1634,30 +1646,30 @@ def create_trisurf(x, y, z, simplices, colormap=None, color_func=None,
16341646
:param (array) z: data values of z in a 1D array
16351647
:param (array) simplices: an array of shape (ntri, 3) where ntri is
16361648
the number of triangles in the triangularization. Each row of the
1637-
array contains the indicies of the verticies of each triangle.
1649+
array contains the indicies of the verticies of each triangle
16381650
:param (str|list) colormap: either a plotly scale name, or a list
16391651
containing 2 triplets. These triplets must be of the form (a,b,c)
16401652
or 'rgb(x,y,z)' where a,b,c belong to the interval [0,1] and x,y,z
16411653
belong to [0,255]
16421654
:param (function|list) color_func: The parameter that determines the
16431655
coloring of the surface. Takes either a function with 3 arguments
16441656
x, y, z or a list/array of color values the same length as
1645-
simplices. If set to None, color will only depend on the z axis.
1657+
simplices. If set to None, color will only depend on the z axis
16461658
:param (str) title: title of the plot
16471659
:param (bool) plot_edges: determines if the triangles on the trisurf
16481660
are visible
16491661
:param (bool) showbackground: makes background in plot visible
16501662
:param (str) backgroundcolor: color of background. Takes a string of
1651-
the form 'rgb(x,y,z)' x,y,z are between 0 and 255 inclusive.
1663+
the form 'rgb(x,y,z)' x,y,z are between 0 and 255 inclusive
16521664
:param (str) gridcolor: color of the gridlines besides the axes. Takes
16531665
a string of the form 'rgb(x,y,z)' x,y,z are between 0 and 255
1654-
inclusive.
1666+
inclusive
16551667
:param (str) zerolinecolor: color of the axes. Takes a string of the
1656-
form 'rgb(x,y,z)' x,y,z are between 0 and 255 inclusive.
1668+
form 'rgb(x,y,z)' x,y,z are between 0 and 255 inclusive
16571669
:param (int|float) height: the height of the plot (in pixels)
16581670
:param (int|float) width: the width of the plot (in pixels)
16591671
:param (dict) aspectratio: a dictionary of the aspect ratio values for
1660-
the x, y and z axes. 'x', 'y' and 'z' take (int|float) values.
1672+
the x, y and z axes. 'x', 'y' and 'z' take (int|float) values
16611673
16621674
Example 1: Sphere
16631675
```

0 commit comments

Comments
 (0)