diff --git a/examples/pylab_examples/tripcolor_demo.py b/examples/pylab_examples/tripcolor_demo.py index 0817c63c53b1..f97910af7d89 100644 --- a/examples/pylab_examples/tripcolor_demo.py +++ b/examples/pylab_examples/tripcolor_demo.py @@ -32,19 +32,19 @@ mask = np.where(xmid*xmid + ymid*ymid < min_radius*min_radius, 1, 0) triang.set_mask(mask) -# pcolor plot. +# tripcolor plot. plt.figure() plt.gca().set_aspect('equal') plt.tripcolor(triang, z, shading='flat', cmap=plt.cm.rainbow) plt.colorbar() -plt.title('tripcolor of Delaunay triangulation: flat') +plt.title('tripcolor of Delaunay triangulation, flat shading') # Illustrate Gouraud shading. plt.figure() plt.gca().set_aspect('equal') plt.tripcolor(triang, z, shading='gouraud', cmap=plt.cm.rainbow) plt.colorbar() -plt.title('tripcolor with Gouraud shading') +plt.title('tripcolor of Delaunay triangulation, gouraud shading') # You can specify your own triangulation rather than perform a Delaunay @@ -70,9 +70,6 @@ [-0.057,0.916],[-0.025,0.933],[-0.077,0.990],[-0.059,0.993] ]) x = xy[:,0]*180/3.14159 y = xy[:,1]*180/3.14159 -x0 = -5 -y0 = 52 -z = np.exp(-0.01*( (x-x0)*(x-x0) + (y-y0)*(y-y0) )) triangles = np.asarray([ [67,66, 1],[65, 2,66],[ 1,66, 2],[64, 2,65],[63, 3,64],[60,59,57], @@ -90,14 +87,21 @@ [32,31,33],[39,38,72],[33,72,38],[33,38,34],[37,35,38],[34,38,35], [35,37,36] ]) +xmid = x[triangles].mean(axis=1) +ymid = y[triangles].mean(axis=1) +x0 = -5 +y0 = 52 +zfaces = np.exp(-0.01*( (xmid-x0)*(xmid-x0) + (ymid-y0)*(ymid-y0) )) + # Rather than create a Triangulation object, can simply pass x, y and triangles # arrays to tripcolor directly. It would be better to use a Triangulation object # if the same triangulation was to be used more than once to save duplicated # calculations. +# Can specify one color value per face rather than one per point by using the +# facecolors kwarg. plt.figure() plt.gca().set_aspect('equal') -plt.tripcolor(x, y, triangles, z, shading='flat', edgecolors='k', - cmap='summer') +plt.tripcolor(x, y, triangles, facecolors=zfaces, edgecolors='k') plt.colorbar() plt.title('tripcolor of user-specified triangulation') plt.xlabel('Longitude (degrees)') diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 3860b031e319..312d30a76f43 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1021,6 +1021,7 @@ def tk_window_focus(): 'matplotlib.tests.test_legend', 'matplotlib.tests.test_colorbar', 'matplotlib.tests.test_patches', + 'matplotlib.tests.test_triangulation' ] def test(verbosity=1): diff --git a/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.pdf b/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.pdf new file mode 100644 index 000000000000..ac2c3873949c Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.png b/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.png new file mode 100644 index 000000000000..1e5e83701ffb Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.svg b/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.svg new file mode 100644 index 000000000000..f9196f76c987 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_triangulation/tripcolor1.svg @@ -0,0 +1,1229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/test_triangulation.py b/lib/matplotlib/tests/test_triangulation.py index 52279758e074..81f922a0b225 100644 --- a/lib/matplotlib/tests/test_triangulation.py +++ b/lib/matplotlib/tests/test_triangulation.py @@ -1,8 +1,10 @@ import numpy as np +import matplotlib.pyplot as plt import matplotlib.tri as mtri import matplotlib.delaunay as mdel from nose.tools import assert_equal from numpy.testing import assert_array_equal, assert_array_almost_equal +from matplotlib.testing.decorators import image_comparison def test_delaunay(): # No duplicate points. @@ -94,3 +96,29 @@ def test_delaunay_duplicate_points(): mpl_triang.edges) assert_array_equal(del_triang.edge_db, converted_indices) +@image_comparison(baseline_images=['tripcolor1']) +def test_tripcolor(): + x = np.asarray([0, 0.5, 1, 0, 0.5, 1, 0, 0.5, 1, 0.75]) + y = np.asarray([0, 0, 0, 0.5, 0.5, 0.5, 1, 1, 1, 0.75]) + triangles = np.asarray([ + [0, 1, 3], [1, 4, 3], + [1, 2, 4], [2, 5, 4], + [3, 4, 6], [4, 7, 6], + [4, 5, 9], [7, 4, 9], [8, 7, 9], [5, 8, 9] ]) + + # Triangulation with same number of points and triangles. + triang = mtri.Triangulation(x, y, triangles) + + Cpoints = x + 0.5*y + + xmid = x[triang.triangles].mean(axis=1) + ymid = y[triang.triangles].mean(axis=1) + Cfaces = 0.5*xmid + ymid + + plt.subplot(121) + plt.tripcolor(triang, Cpoints, edgecolors='k') + plt.title('point colors') + + plt.subplot(122) + plt.tripcolor(triang, facecolors=Cfaces, edgecolors='k') + plt.title('facecolors') diff --git a/lib/matplotlib/tri/tripcolor.py b/lib/matplotlib/tri/tripcolor.py index aaf7896be39d..b65290bcaa6a 100644 --- a/lib/matplotlib/tri/tripcolor.py +++ b/lib/matplotlib/tri/tripcolor.py @@ -28,12 +28,20 @@ def tripcolor(ax, *args, **kwargs): :class:`~matplotlib.tri.Triangulation` for a explanation of these possibilities. - The next argument must be *C*, the array of color values, one per - point in the triangulation. - - *shading* may be 'flat', 'faceted' or 'gouraud'. If *shading* is - 'flat' or 'faceted', the colors used for each triangle are from - the mean C of the triangle's three points. + The next argument must be *C*, the array of color values, either + one per point in the triangulation if color values are defined at + points, or one per triangle in the triangulation if color values + are defined at triangles. If there are the same number of points + and triangles in the triangulation it is assumed that color + values are defined at points; to force the use of color values at + triangles use the kwarg *facecolors*=C instead of just *C*. + + *shading* may be 'flat' (the default) or 'gouraud'. If *shading* + is 'flat' and C values are defined at points, the color values + used for each triangle are from the mean C of the triangle's + three points. If *shading* is 'gouraud' then color values must be + defined at points. *shading* of 'faceted' is deprecated; + please use *edgecolors* instead. The remaining kwargs are the same as for :meth:`~matplotlib.axes.Axes.pcolor`. @@ -50,35 +58,78 @@ def tripcolor(ax, *args, **kwargs): vmin = kwargs.pop('vmin', None) vmax = kwargs.pop('vmax', None) shading = kwargs.pop('shading', 'flat') + facecolors = kwargs.pop('facecolors', None) tri, args, kwargs = Triangulation.get_from_args_and_kwargs(*args, **kwargs) - x = tri.x - y = tri.y - triangles = tri.get_masked_triangles() - C = np.asarray(args[0]) - if C.shape != x.shape: - raise ValueError('C array must have same length as triangulation x and' - ' y arrays') + # C is the colors array, defined at either points or faces (i.e. triangles). + # If facecolors is None, C are defined at points. + # If facecolors is not None, C are defined at faces. + if facecolors is not None: + C = facecolors + else: + C = np.asarray(args[0]) + + # If there are a different number of points and triangles in the + # triangulation, can omit facecolors kwarg as it is obvious from + # length of C whether it refers to points or faces. + # Do not do this for gouraud shading. + if (facecolors is None and len(C) == len(tri.triangles) and + len(C) != len(tri.x) and shading != 'gouraud'): + facecolors = C + + # Check length of C is OK. + if ( (facecolors is None and len(C) != len(tri.x)) or + (facecolors is not None and len(C) != len(tri.triangles)) ): + raise ValueError('Length of color values array must be the same ' + 'as either the number of triangulation points ' + 'or triangles') + + + # Handling of linewidths, shading, edgecolors and antialiased as + # in Axes.pcolor + linewidths = (0.25,) + if 'linewidth' in kwargs: + kwargs['linewidths'] = kwargs.pop('linewidth') + kwargs.setdefault('linewidths', linewidths) + + if shading == 'faceted': # Deprecated. + edgecolors = 'k', + else: + edgecolors = 'none' + if 'edgecolor' in kwargs: + kwargs['edgecolors'] = kwargs.pop('edgecolor') + ec = kwargs.setdefault('edgecolors', edgecolors) + + if 'antialiased' in kwargs: + kwargs['antialiaseds'] = kwargs.pop('antialiased') + if 'antialiaseds' not in kwargs and ec.lower() == "none": + kwargs['antialiaseds'] = False + if shading == 'gouraud': + if facecolors is not None: + raise ValueError('Gouraud shading does not support the use ' + 'of facecolors kwarg') + if len(C) != len(tri.x): + raise ValueError('For gouraud shading, the length of color ' + 'values array must be the same as the ' + 'number of triangulation points') collection = TriMesh(tri, **kwargs) else: - if shading == 'faceted': - edgecolors = (0,0,0,1), - linewidths = (0.25,) - else: - edgecolors = 'face' - linewidths = (1.0,) - kwargs.setdefault('edgecolors', edgecolors) - kwargs.setdefault('antialiaseds', (0,)) - kwargs.setdefault('linewidths', linewidths) - # Vertices of triangles. - verts = np.concatenate((x[triangles][...,np.newaxis], - y[triangles][...,np.newaxis]), axis=2) - # Color values, one per triangle, mean of the 3 vertex color values. - C = C[triangles].mean(axis=1) + maskedTris = tri.get_masked_triangles() + verts = np.concatenate((tri.x[maskedTris][...,np.newaxis], + tri.y[maskedTris][...,np.newaxis]), axis=2) + + # Color values. + if facecolors is None: + # One color per triangle, the mean of the 3 vertex color values. + C = C[maskedTris].mean(axis=1) + elif tri.mask is not None: + # Remove color values of masked triangles. + C = C.compress(1-tri.mask) + collection = PolyCollection(verts, **kwargs) collection.set_alpha(alpha)