From f8ec9aa84aaf3eae3ecdfd4b74e87fa5e9ff2417 Mon Sep 17 00:00:00 2001 From: Manuel GOACOLOU Date: Wed, 18 Sep 2013 00:22:06 +0200 Subject: [PATCH 01/25] add rotation capabilities for scatter plot and PathCollection --- .../pylab_examples/scatter_rotate_symbol.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 examples/pylab_examples/scatter_rotate_symbol.py diff --git a/examples/pylab_examples/scatter_rotate_symbol.py b/examples/pylab_examples/scatter_rotate_symbol.py new file mode 100644 index 000000000000..845d5194c8f4 --- /dev/null +++ b/examples/pylab_examples/scatter_rotate_symbol.py @@ -0,0 +1,17 @@ +import matplotlib.pyplot as plt +from numpy import arange, pi, cos, sin +from numpy.random import rand + +# unit area ellipse +rx, ry = 3., 1. +area = rx * ry * pi +theta = arange(0, 2*pi+0.01, 0.1) +verts = list(zip(rx/area*cos(theta), ry/area*sin(theta))) + +x,y,s,c = rand(4, 30) +s*= 10**2. + +fig, ax = plt.subplots() +ax.scatter(x,y,s,c,marker=None,verts =verts) + +plt.show() From 626dd6f5f381d80d0ece3d4b37a926fb8d8da1c6 Mon Sep 17 00:00:00 2001 From: Manuel GOACOLOU Date: Wed, 18 Sep 2013 00:24:09 +0200 Subject: [PATCH 02/25] add rotation capabilities for scatter plot and PathCollection --- .../pylab_examples/scatter_rotate_symbol.py | 14 +++---- lib/matplotlib/axes.py | 11 ++++-- lib/matplotlib/collections.py | 37 +++++++++++++++++-- lib/matplotlib/pyplot.py | 4 +- 4 files changed, 50 insertions(+), 16 deletions(-) diff --git a/examples/pylab_examples/scatter_rotate_symbol.py b/examples/pylab_examples/scatter_rotate_symbol.py index 845d5194c8f4..ed991da07d9b 100644 --- a/examples/pylab_examples/scatter_rotate_symbol.py +++ b/examples/pylab_examples/scatter_rotate_symbol.py @@ -1,17 +1,17 @@ import matplotlib.pyplot as plt -from numpy import arange, pi, cos, sin +from numpy import arange, pi, rad2deg from numpy.random import rand +from matplotlib.markers import TICKRIGHT -# unit area ellipse rx, ry = 3., 1. area = rx * ry * pi -theta = arange(0, 2*pi+0.01, 0.1) -verts = list(zip(rx/area*cos(theta), ry/area*sin(theta))) +theta = rad2deg(arange(0, 2*pi+0.01, 0.1)) -x,y,s,c = rand(4, 30) -s*= 10**2. + +x, y, s, c = rand(4, 30) +s *= 20**2. fig, ax = plt.subplots() -ax.scatter(x,y,s,c,marker=None,verts =verts) +ax.scatter(x, y, s, c, marker=TICKRIGHT, a=theta) plt.show() diff --git a/lib/matplotlib/axes.py b/lib/matplotlib/axes.py index 8eebb9608148..b23c32aa1309 100644 --- a/lib/matplotlib/axes.py +++ b/lib/matplotlib/axes.py @@ -6168,7 +6168,7 @@ def dopatch(xs, ys): medians=medians, fliers=fliers) @docstring.dedent_interpd - def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None, + def scatter(self, x, y, s=20, c='b', marker='o', a=0, cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, verts=None, **kwargs): """ @@ -6196,6 +6196,9 @@ def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None, See `~matplotlib.markers` for more information on the different styles of markers scatter supports. + a : calar or array_like, shape (n, ), optional, default: 0 degrees CCW + from X axis + cmap : `~matplotlib.colors.Colormap`, optional, default: None A `~matplotlib.colors.Colormap` instance or registered name. `cmap` is only used if `c` is an array of floats. If None, @@ -6256,6 +6259,7 @@ def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None, raise ValueError("x and y must be the same size") s = np.ma.ravel(s) # This doesn't have to match x, y in size. + a = np.ma.ravel(a) # This doesn't have to match x, y in size. c_is_stringy = is_string_like(c) or is_sequence_of_strings(c) if not c_is_stringy: @@ -6263,9 +6267,10 @@ def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None, if c.size == x.size: c = np.ma.ravel(c) - x, y, s, c = cbook.delete_masked_points(x, y, s, c) + x, y, s, c, a = cbook.delete_masked_points(x, y, s, c, a) scales = s # Renamed for readability below. + angles = a # Renamed for readability below. if c_is_stringy: colors = mcolors.colorConverter.to_rgba_array(c, alpha) @@ -6299,7 +6304,7 @@ def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None, edgecolors = 'face' collection = mcoll.PathCollection( - (path,), scales, + (path,), scales, angles, facecolors=colors, edgecolors=edgecolors, linewidths=linewidths, diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index a40546da2770..c3238b066e57 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -687,7 +687,7 @@ class PathCollection(Collection): This is the most basic :class:`Collection` subclass. """ @docstring.dedent_interpd - def __init__(self, paths, sizes=None, **kwargs): + def __init__(self, paths, sizes=None, angles=None, **kwargs): """ *paths* is a sequence of :class:`matplotlib.path.Path` instances. @@ -698,6 +698,20 @@ def __init__(self, paths, sizes=None, **kwargs): Collection.__init__(self, **kwargs) self.set_paths(paths) self._sizes = sizes + self._angles = angles + self.__check_parameters() + + def __check_parameters(self): + if self._sizes is not None and self._angles is not None and self._sizes.size != self._angles.size: + ar_resize = np.resize + if isinstance(self._sizes, np.ma.core.MaskedArray): + ar_resize = np.ma.resize + + # make same size + if self._sizes.size > self._angles.size: + self._angles = ar_resize(self._angles, self._sizes.shape) + else: + self._sizes = ar_resize(self._sizes, self._angles.shape) def set_paths(self, paths): self._paths = paths @@ -708,13 +722,28 @@ def get_paths(self): def get_sizes(self): return self._sizes + def get_angles(self): + return self._angles + @allow_rasterization def draw(self, renderer): - if self._sizes is not None: + if self._sizes is not None and self._angles is not None: + if self._sizes.size != self._angles.size: + raise ValueError("sizes and angles must have the same size") self._transforms = [ transforms.Affine2D().scale( - (np.sqrt(x) * self.figure.dpi / 72.0)) - for x in self._sizes] + (np.sqrt(s) * self.figure.dpi / 72.0)).rotate(a) + for s, a in zip(self._sizes, self._angles)] + elif self._sizes is not None: + self._transforms = [ + transforms.Affine2D().scale( + (np.sqrt(s) * self.figure.dpi / 72.0)) + for s in self._sizes] + elif self._angles is not None: + self._transforms = [ + transforms.Affine2D().rotate(a) + for a in self._angles] + return Collection.draw(self, renderer) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index e650d13b0573..6476078eda2c 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -3072,7 +3072,7 @@ def quiverkey(*args, **kw): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.scatter) -def scatter(x, y, s=20, c='b', marker='o', cmap=None, norm=None, vmin=None, +def scatter(x, y, s=20, c='b', marker='o', a=0, cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, verts=None, hold=None, **kwargs): ax = gca() @@ -3082,7 +3082,7 @@ def scatter(x, y, s=20, c='b', marker='o', cmap=None, norm=None, vmin=None, if hold is not None: ax.hold(hold) try: - ret = ax.scatter(x, y, s=s, c=c, marker=marker, cmap=cmap, norm=norm, + ret = ax.scatter(x, y, s=s, c=c, marker=marker, a=a, cmap=cmap, norm=norm, vmin=vmin, vmax=vmax, alpha=alpha, linewidths=linewidths, verts=verts, **kwargs) draw_if_interactive() From b58bfae5275bc6e88f62b408d175da0310e6ebd2 Mon Sep 17 00:00:00 2001 From: Manuel GOACOLOU Date: Wed, 18 Sep 2013 10:25:14 +0200 Subject: [PATCH 03/25] PEP8 line too long --- lib/matplotlib/collections.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index c3238b066e57..905d0a6a152d 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -702,7 +702,10 @@ def __init__(self, paths, sizes=None, angles=None, **kwargs): self.__check_parameters() def __check_parameters(self): - if self._sizes is not None and self._angles is not None and self._sizes.size != self._angles.size: + if self._sizes is not None \ + and self._angles is not None \ + and self._sizes.size != self._angles.size: + ar_resize = np.resize if isinstance(self._sizes, np.ma.core.MaskedArray): ar_resize = np.ma.resize From 2742c1b56358c4c579d28c430c6f65f6c3a39ff5 Mon Sep 17 00:00:00 2001 From: Manuel GOACOLOU Date: Wed, 18 Sep 2013 10:47:16 +0200 Subject: [PATCH 04/25] PathCollection docstring and code revue from pelson --- lib/matplotlib/collections.py | 42 ++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 905d0a6a152d..0d5592b21866 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -692,6 +692,10 @@ def __init__(self, paths, sizes=None, angles=None, **kwargs): *paths* is a sequence of :class:`matplotlib.path.Path` instances. + *sizes* is an array of length 1 or more + + *angless* is an array of length 1 or more + %(Collection)s """ @@ -699,9 +703,13 @@ def __init__(self, paths, sizes=None, angles=None, **kwargs): self.set_paths(paths) self._sizes = sizes self._angles = angles - self.__check_parameters() + self._check_parameters() - def __check_parameters(self): + def _check_parameters(self): + """ + Check the sizes and array dimention to make them the same size + Needed for drawing them efficiently + """ if self._sizes is not None \ and self._angles is not None \ and self._sizes.size != self._angles.size: @@ -710,29 +718,53 @@ def __check_parameters(self): if isinstance(self._sizes, np.ma.core.MaskedArray): ar_resize = np.ma.resize - # make same size + # Make sizes array and angles array same size if self._sizes.size > self._angles.size: self._angles = ar_resize(self._angles, self._sizes.shape) else: self._sizes = ar_resize(self._sizes, self._angles.shape) def set_paths(self, paths): + """ + update the paths sequence + """ self._paths = paths def get_paths(self): + """ + return the paths sequence + """ return self._paths + def set_sizes(self, sizes): + """ + update sizes array check array size + """ + self._sizes = sizes + self._check_parameters() + def get_sizes(self): + """ + return the sizes array + """ return self._sizes + def set_angles(self, angles): + """ + update angles array check array size + """ + self._angles = angles + self._check_parameters() + def get_angles(self): + """ + return the angle array + """ return self._angles @allow_rasterization def draw(self, renderer): if self._sizes is not None and self._angles is not None: - if self._sizes.size != self._angles.size: - raise ValueError("sizes and angles must have the same size") self._transforms = [ transforms.Affine2D().scale( (np.sqrt(s) * self.figure.dpi / 72.0)).rotate(a) From e376c4679bdcc2a04014b7c79486f033d7ab5cb8 Mon Sep 17 00:00:00 2001 From: Manuel GOACOLOU Date: Wed, 18 Sep 2013 10:54:49 +0200 Subject: [PATCH 05/25] Axes.scatter and pyplot.scatter updates with code revue from pelson --- lib/matplotlib/axes.py | 11 +++++------ lib/matplotlib/pyplot.py | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/axes.py b/lib/matplotlib/axes.py index b23c32aa1309..79e817b320ef 100644 --- a/lib/matplotlib/axes.py +++ b/lib/matplotlib/axes.py @@ -6168,7 +6168,7 @@ def dopatch(xs, ys): medians=medians, fliers=fliers) @docstring.dedent_interpd - def scatter(self, x, y, s=20, c='b', marker='o', a=0, cmap=None, norm=None, + def scatter(self, x, y, s=20, c='b', marker='o', angles=0, cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, verts=None, **kwargs): """ @@ -6196,8 +6196,8 @@ def scatter(self, x, y, s=20, c='b', marker='o', a=0, cmap=None, norm=None, See `~matplotlib.markers` for more information on the different styles of markers scatter supports. - a : calar or array_like, shape (n, ), optional, default: 0 degrees CCW - from X axis + angles : scalar or array_like, shape (n, ), optional, default: 0 + degrees CCW from X axis cmap : `~matplotlib.colors.Colormap`, optional, default: None A `~matplotlib.colors.Colormap` instance or registered name. @@ -6259,7 +6259,7 @@ def scatter(self, x, y, s=20, c='b', marker='o', a=0, cmap=None, norm=None, raise ValueError("x and y must be the same size") s = np.ma.ravel(s) # This doesn't have to match x, y in size. - a = np.ma.ravel(a) # This doesn't have to match x, y in size. + angles = np.ma.ravel(angles) # This doesn't have to match x, y in size. c_is_stringy = is_string_like(c) or is_sequence_of_strings(c) if not c_is_stringy: @@ -6267,10 +6267,9 @@ def scatter(self, x, y, s=20, c='b', marker='o', a=0, cmap=None, norm=None, if c.size == x.size: c = np.ma.ravel(c) - x, y, s, c, a = cbook.delete_masked_points(x, y, s, c, a) + x, y, s, c, angles = cbook.delete_masked_points(x, y, s, c, angles) scales = s # Renamed for readability below. - angles = a # Renamed for readability below. if c_is_stringy: colors = mcolors.colorConverter.to_rgba_array(c, alpha) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 6476078eda2c..cb1ff4d59f54 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -3072,7 +3072,7 @@ def quiverkey(*args, **kw): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.scatter) -def scatter(x, y, s=20, c='b', marker='o', a=0, cmap=None, norm=None, vmin=None, +def scatter(x, y, s=20, c='b', marker='o', angles=0, cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, verts=None, hold=None, **kwargs): ax = gca() @@ -3082,7 +3082,7 @@ def scatter(x, y, s=20, c='b', marker='o', a=0, cmap=None, norm=None, vmin=None, if hold is not None: ax.hold(hold) try: - ret = ax.scatter(x, y, s=s, c=c, marker=marker, a=a, cmap=cmap, norm=norm, + ret = ax.scatter(x, y, s=s, c=c, marker=marker, angles=angles, cmap=cmap, norm=norm, vmin=vmin, vmax=vmax, alpha=alpha, linewidths=linewidths, verts=verts, **kwargs) draw_if_interactive() From 9d4b4cf0baaf843f653b254fd3170c5953a834f5 Mon Sep 17 00:00:00 2001 From: Manuel GOACOLOU Date: Wed, 18 Sep 2013 14:38:08 +0200 Subject: [PATCH 06/25] update example --- examples/pylab_examples/scatter_rotate_symbol.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/examples/pylab_examples/scatter_rotate_symbol.py b/examples/pylab_examples/scatter_rotate_symbol.py index ed991da07d9b..02adc62a0fc2 100644 --- a/examples/pylab_examples/scatter_rotate_symbol.py +++ b/examples/pylab_examples/scatter_rotate_symbol.py @@ -1,17 +1,15 @@ +import numpy as np import matplotlib.pyplot as plt -from numpy import arange, pi, rad2deg -from numpy.random import rand from matplotlib.markers import TICKRIGHT rx, ry = 3., 1. -area = rx * ry * pi -theta = rad2deg(arange(0, 2*pi+0.01, 0.1)) +area = rx * ry * np.pi +angles = np.linspace(0., 360., 30.) +x, y, sizes, colors = np.random.rand(4, 30) +sizes *= 20**2. -x, y, s, c = rand(4, 30) -s *= 20**2. - -fig, ax = plt.subplots() -ax.scatter(x, y, s, c, marker=TICKRIGHT, a=theta) +plt.scatter(x, y, sizes, colors, marker="o",zorder=2) +plt.scatter(x, y, 2.5*sizes, colors, marker=TICKRIGHT, angles=angles, zorder=2) plt.show() From 23b79d88710661eada6bb607e9fe0c59852bb944 Mon Sep 17 00:00:00 2001 From: Damon McDougall Date: Fri, 21 Jun 2013 21:09:53 -0500 Subject: [PATCH 07/25] Update stackplot docs for legend capabilities The docs now state that creation of a proxy artist is the preferred method for creating legends on stackplots. This is because stackplot creates PolyCollection objects which are not supported by the legend --- lib/matplotlib/stackplot.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/matplotlib/stackplot.py b/lib/matplotlib/stackplot.py index 99942a340b6e..c7a70971c6b2 100644 --- a/lib/matplotlib/stackplot.py +++ b/lib/matplotlib/stackplot.py @@ -43,6 +43,11 @@ def stackplot(axes, x, *args, **kwargs): Returns *r* : A list of :class:`~matplotlib.collections.PolyCollection`, one for each element in the stacked area plot. + + Note that :class:`~matplotlib.legend.Legend` does not support + :class:`~matplotlib.collections.PolyCollection` objects. To create a + legend on a stackplot, use a proxy artist: + http://matplotlib.org/users/legend_guide.html#using-proxy-artist """ if len(args) == 1: From 3527d36909d49e83df4bdb52808c834586144ffe Mon Sep 17 00:00:00 2001 From: Damon McDougall Date: Fri, 28 Jun 2013 17:28:00 -0500 Subject: [PATCH 08/25] Add warning for mixing AnchoredText with bad kwarg Mixing 'ha' or 'va' with AnchoredText produces bad output, so add a warning in this case. --- lib/matplotlib/offsetbox.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/matplotlib/offsetbox.py b/lib/matplotlib/offsetbox.py index 743fccc2bc25..b21f6ddbfb68 100644 --- a/lib/matplotlib/offsetbox.py +++ b/lib/matplotlib/offsetbox.py @@ -16,6 +16,7 @@ from __future__ import print_function +import warnings import matplotlib.transforms as mtransforms import matplotlib.artist as martist import matplotlib.text as mtext @@ -1098,6 +1099,12 @@ def __init__(self, s, loc, pad=0.4, borderpad=0.5, prop=None, **kwargs): other keyword parameters of AnchoredOffsetbox are also allowed. """ + propkeys = prop.keys() + badkwargs = ('ha', 'horizontalalignment', 'va', 'verticalalignment') + if set(badkwargs) & set(propkeys): + warnings.warn("Mixing horizontalalignment or verticalalignment " + "with AnchoredText is not supported.") + self.txt = TextArea(s, textprops=prop, minimumdescent=False) fp = self.txt._text.get_fontproperties() From b9fbc3ef4cd983e8ab910ecc602cd0c5239de9c4 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Tue, 2 Jul 2013 14:55:17 -0400 Subject: [PATCH 09/25] Clear the Python exception when the transformation passed in does not parse as an affine transform. --- src/agg_py_transforms.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/agg_py_transforms.cpp b/src/agg_py_transforms.cpp index 739512605b99..7e85ec1b7608 100644 --- a/src/agg_py_transforms.cpp +++ b/src/agg_py_transforms.cpp @@ -31,8 +31,10 @@ py_to_agg_transformation_matrix(PyObject* obj, bool errors = true) try { matrix = (PyArrayObject*) PyArray_FromObject(obj, PyArray_DOUBLE, 2, 2); - if (!matrix) + if (!matrix) { + PyErr_Clear(); throw std::exception(); + } } catch (...) { From 323e36128a98dd372821a964af14ca0ffd82bf4b Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Tue, 2 Jul 2013 14:55:29 -0400 Subject: [PATCH 10/25] Pass an instance, rather than a type, to cleanup_path --- lib/matplotlib/tests/test_simplification.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_simplification.py b/lib/matplotlib/tests/test_simplification.py index f5247ce17ad4..fc9afb899aa0 100644 --- a/lib/matplotlib/tests/test_simplification.py +++ b/lib/matplotlib/tests/test_simplification.py @@ -152,7 +152,7 @@ def test_start_with_moveto(): verts = np.fromstring(decodebytes(data), dtype=' Date: Tue, 2 Jul 2013 14:55:50 -0400 Subject: [PATCH 11/25] Use a regular expression to handle the different output of integers on Unix and Windows. --- lib/matplotlib/tests/test_contour.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/matplotlib/tests/test_contour.py b/lib/matplotlib/tests/test_contour.py index 120b4843e436..3cc2416dddec 100644 --- a/lib/matplotlib/tests/test_contour.py +++ b/lib/matplotlib/tests/test_contour.py @@ -3,6 +3,8 @@ from matplotlib.testing.decorators import cleanup, image_comparison from matplotlib import pyplot as plt +import re + @cleanup def test_contour_shape_1d_valid(): @@ -97,14 +99,18 @@ def test_contour_shape_mismatch_4(): ax.contour(b, g, z) except TypeError as exc: print exc.args[0] - assert exc.args[0] == 'Shape of x does not match that of z: ' + \ - 'found (9, 9) instead of (9, 10).' + assert re.match( + r'Shape of x does not match that of z: ' + + r'found \(9L?, 9L?\) instead of \(9L?, 10L?\)\.', + exc.args[0]) is not None try: ax.contour(g, b, z) except TypeError as exc: - assert exc.args[0] == 'Shape of y does not match that of z: ' + \ - 'found (9, 9) instead of (9, 10).' + assert re.match( + r'Shape of y does not match that of z: ' + + r'found \(9L?, 9L?\) instead of \(9L?, 10L?\)\.', + exc.args[0]) is not None @cleanup @@ -157,23 +163,23 @@ def test_given_colors_levels_and_extends(): _, axes = plt.subplots(2, 4) data = np.arange(12).reshape(3, 4) - + colors = ['red', 'yellow', 'pink', 'blue', 'black'] levels = [2, 4, 8, 10] - + for i, ax in enumerate(axes.flatten()): plt.sca(ax) - + filled = i % 2 == 0. extend = ['neither', 'min', 'max', 'both'][i // 2] - + if filled: last_color = -1 if extend in ['min', 'max'] else None plt.contourf(data, colors=colors[:last_color], levels=levels, extend=extend) else: last_level = -1 if extend == 'both' else None plt.contour(data, colors=colors, levels=levels[:last_level], extend=extend) - + plt.colorbar() From ea80948cca52550b2348c6baed606ffd3a3a6327 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Tue, 2 Jul 2013 14:30:38 -0400 Subject: [PATCH 12/25] Fix the link to the nosedocs and add a little more information --- tests.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests.py b/tests.py index a78c9bb2b194..677d18ff6b06 100755 --- a/tests.py +++ b/tests.py @@ -1,9 +1,13 @@ #!/usr/bin/env python # # This allows running the matplotlib tests from the command line: e.g. -# python tests.py -v -d -# See http://somethingaboutorange.com/mrl/projects/nose/1.0.0/usage.html -# for options. +# +# $ python tests.py -v -d +# +# The arguments are identical to the arguments accepted by nosetests. +# +# See https://nose.readthedocs.org/ for a detailed description of +# these options. import os import time From d9d16ae2e5fd6bd37b27c7fa9cf192d408b1eec1 Mon Sep 17 00:00:00 2001 From: Christoph Gohlke Date: Sat, 1 Jun 2013 00:14:21 -0600 Subject: [PATCH 13/25] Fix building Python integers from size_t values Use the `n` format specifier instead of `l` to build Python integer or long integer objects from size_t values. --- src/_image.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/_image.cpp b/src/_image.cpp index 9e77ea9613e9..208cf5cc92e6 100644 --- a/src/_image.cpp +++ b/src/_image.cpp @@ -202,10 +202,10 @@ Image::as_rgba_str(const Py::Tuple& args, const Py::Dict& kwargs) std::pair bufpair = _get_output_buffer(); #if PY3K - Py::Object ret = Py::asObject(Py_BuildValue("lly#", rowsOut, colsOut, + Py::Object ret = Py::asObject(Py_BuildValue("nny#", rowsOut, colsOut, bufpair.first, colsOut * rowsOut * 4)); #else - Py::Object ret = Py::asObject(Py_BuildValue("lls#", rowsOut, colsOut, + Py::Object ret = Py::asObject(Py_BuildValue("nns#", rowsOut, colsOut, bufpair.first, colsOut * rowsOut * 4)); #endif @@ -272,7 +272,7 @@ Image::color_conv(const Py::Tuple& args) } #endif - PyObject* o = Py_BuildValue("llN", rowsOut, colsOut, py_buffer); + PyObject* o = Py_BuildValue("nnN", rowsOut, colsOut, py_buffer); return Py::asObject(o); } @@ -290,7 +290,7 @@ Image::buffer_rgba(const Py::Tuple& args) args.verify_length(0); int row_len = colsOut * 4; - PyObject* o = Py_BuildValue("lls#", rowsOut, colsOut, + PyObject* o = Py_BuildValue("nns#", rowsOut, colsOut, rbufOut, row_len * rowsOut); return Py::asObject(o); } From f605d3663dedde15398aa7ecca1b23b82f618417 Mon Sep 17 00:00:00 2001 From: Phil Elson Date: Mon, 11 Mar 2013 16:51:30 +0000 Subject: [PATCH 14/25] Updated some of the documentation information. --- doc/conf.py | 2 +- doc/devel/documenting_mpl.rst | 2 +- doc/users/license.rst | 22 ++++++------ lib/matplotlib/afm.py | 43 +++++++++--------------- lib/matplotlib/backends/backend_wxagg.py | 23 +++---------- lib/matplotlib/cbook.py | 2 +- 6 files changed, 34 insertions(+), 60 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 54e90d21745a..169047573279 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -58,7 +58,7 @@ # General substitutions. project = 'Matplotlib' -copyright = '2013 John Hunter, Darren Dale, Eric Firing, Michael Droettboom and the matplotlib development team' +copyright = '2002 - 2013 Michael Droettboom and the matplotlib development team' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. diff --git a/doc/devel/documenting_mpl.rst b/doc/devel/documenting_mpl.rst index f43977cb4008..27309e4b4e2c 100644 --- a/doc/devel/documenting_mpl.rst +++ b/doc/devel/documenting_mpl.rst @@ -351,7 +351,7 @@ An example save command to generate a movie looks like this ani.save('double_pendulum.mp4', fps=15) -Contact John Hunter for the login password to upload youtube videos of +Contact Michael Droettboom for the login password to upload youtube videos of google docs to the mplgithub account. .. _referring-to-mpl-docs: diff --git a/doc/users/license.rst b/doc/users/license.rst index acf401c29e46..77cbb4a53b83 100644 --- a/doc/users/license.rst +++ b/doc/users/license.rst @@ -17,18 +17,18 @@ licencing choice, see :ref:`license-discussion`. License agreement for matplotlib |version| ============================================== -1. This LICENSE AGREEMENT is between John D. Hunter ("JDH"), and the +1. This LICENSE AGREEMENT is between Michael Droettboom ("MDB"), and the Individual or Organization ("Licensee") accessing and otherwise using matplotlib software in source or binary form and its associated documentation. -2. Subject to the terms and conditions of this License Agreement, JDH +2. Subject to the terms and conditions of this License Agreement, MDB hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use matplotlib |version| -alone or in any derivative version, provided, however, that JDH's -License Agreement and JDH's notice of copyright, i.e., "Copyright (c) -2002-2009 John D. Hunter; All Rights Reserved" are retained in +alone or in any derivative version, provided, however, that MDB's +License Agreement and MDB's notice of copyright, i.e., "Copyright (c) +2002-2013 Michael Droettboom; All Rights Reserved" are retained in matplotlib |version| alone or in any derivative version prepared by Licensee. @@ -38,14 +38,14 @@ make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to matplotlib |version|. -4. JDH is making matplotlib |version| available to Licensee on an "AS -IS" basis. JDH MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, JDH MAKES NO AND +4. MDB is making matplotlib |version| available to Licensee on an "AS +IS" basis. MDB MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, MDB MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB |version| WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. -5. JDH SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB +5. MDB SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB |version| FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING MATPLOTLIB |version|, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF @@ -55,8 +55,8 @@ THE POSSIBILITY THEREOF. breach of its terms and conditions. 7. Nothing in this License Agreement shall be deemed to create any -relationship of agency, partnership, or joint venture between JDH and -Licensee. This License Agreement does not grant permission to use JDH +relationship of agency, partnership, or joint venture between MDB and +Licensee. This License Agreement does not grant permission to use MDB trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. diff --git a/lib/matplotlib/afm.py b/lib/matplotlib/afm.py index ba14a6f7a042..75c7491a3f96 100644 --- a/lib/matplotlib/afm.py +++ b/lib/matplotlib/afm.py @@ -1,23 +1,27 @@ """ This is a python interface to Adobe Font Metrics Files. Although a -number of other python implementations exist (and may be more complete -than mine) I decided not to go with them because either they were -either +number of other python implementations exist, and may be more complete +than this, it was decided not to go with them because they were +either: 1) copyrighted or used a non-BSD compatible license - 2) had too many dependencies and I wanted a free standing lib + 2) had too many dependencies and a free standing lib was needed - 3) Did more than I needed and it was easier to write my own than - figure out how to just get what I needed from theirs + 3) Did more than needed and it was easier to write afresh rather than + figure out how to get just what was needed. -It is pretty easy to use, and requires only built-in python libs:: +It is pretty easy to use, and requires only built-in python libs: - >>> from afm import AFM - >>> fh = open('ptmr8a.afm') - >>> afm = AFM(fh) + >>> from matplotlib import rcParams + >>> import os.path + >>> afm_fname = os.path.join(rcParams['datapath'], + ... 'fonts', 'afm', 'ptmr8a.afm') + >>> + >>> from matplotlib.afm import AFM + >>> afm = AFM(open(afm_fname)) >>> afm.string_width_height('What the heck?') - (6220.0, 683) + (6220.0, 694) >>> afm.get_fontname() 'Times-Roman' >>> afm.get_kern_dist('A', 'f') @@ -26,12 +30,7 @@ -92.0 >>> afm.get_bbox_char('!') [130, -9, 238, 676] - >>> afm.get_bbox_font() - [-168, -218, 1000, 898] - -AUTHOR: - John D. Hunter """ from __future__ import print_function @@ -549,14 +548,4 @@ def get_vertical_stem_width(self): Return the standard vertical stem width as float, or *None* if not specified in AFM file. """ - return self._header.get(b'StdVW', None) - - -if __name__ == '__main__': - #pathname = '/usr/local/lib/R/afm/' - pathname = '/usr/local/share/fonts/afms/adobe' - - for fname in os.listdir(pathname): - with open(os.path.join(pathname, fname)) as fh: - afm = AFM(fh) - w, h = afm.string_width_height('John Hunter is the Man!') + return self._header.get(b'StdVW', None) \ No newline at end of file diff --git a/lib/matplotlib/backends/backend_wxagg.py b/lib/matplotlib/backends/backend_wxagg.py index 5b301a045f04..80276b30d7b0 100644 --- a/lib/matplotlib/backends/backend_wxagg.py +++ b/lib/matplotlib/backends/backend_wxagg.py @@ -1,20 +1,3 @@ -""" - - backend_wxagg.py - - A wxPython backend for Agg. This uses the GUI widgets written by - Jeremy O'Donoghue (jeremy@o-donoghue.com) and the Agg backend by John - Hunter (jdhunter@ace.bsd.uchicago.edu) - - Copyright (C) 2003-5 Jeremy O'Donoghue, John Hunter, Illinois Institute of - Technology - - - License: This work is licensed under the matplotlib license( PSF - compatible). A copy should be included with this source code. - -""" - from __future__ import division, print_function import matplotlib from matplotlib.figure import Figure @@ -26,6 +9,7 @@ draw_if_interactive, show, Toolbar, backend_version import wx + class FigureFrameWxAgg(FigureFrameWx): def get_canvas(self, fig): return FigureCanvasWxAgg(self, -1, fig) @@ -40,6 +24,7 @@ def _get_toolbar(self, statbar): toolbar = None return toolbar + class FigureCanvasWxAgg(FigureCanvasAgg, FigureCanvasWx): """ The FigureCanvas contains the figure and does event handling. @@ -105,6 +90,7 @@ def print_figure(self, filename, *args, **kwargs): if self._isDrawn: self.draw() + class NavigationToolbar2WxAgg(NavigationToolbar2Wx): def get_canvas(self, frame, fig): return FigureCanvasWxAgg(frame, -1, fig) @@ -200,5 +186,4 @@ def _WX28_clipped_agg_as_bitmap(agg, bbox): srcDC.SelectObject(wx.NullBitmap) destDC.SelectObject(wx.NullBitmap) - return destBmp - + return destBmp \ No newline at end of file diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index f44bcb867b2a..771b9654f7e6 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -1617,7 +1617,7 @@ class Grouper(object): >>> grp.join(b, c) >>> grp.join(d, e) >>> sorted(map(tuple, grp)) - [(d, e), (a, b, c)] + [(a, b, c), (d, e)] >>> grp.joined(a, b) True >>> grp.joined(a, c) From 3b32ab31b9ab977693ae7b20a1c343765631f4ae Mon Sep 17 00:00:00 2001 From: Phil Elson Date: Tue, 12 Mar 2013 14:51:44 +0000 Subject: [PATCH 15/25] Updated license initials & copyright notice. Update license --- doc/conf.py | 2 +- doc/users/license.rst | 98 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 88 insertions(+), 12 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 169047573279..4fd5209eed89 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -58,7 +58,7 @@ # General substitutions. project = 'Matplotlib' -copyright = '2002 - 2013 Michael Droettboom and the matplotlib development team' +copyright = '2002 - 2012 John Hunter, Darren Dale, Eric Firing, Michael Droettboom and the matplotlib development team; 2012 - 2013 The matplotlib development team' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. diff --git a/doc/users/license.rst b/doc/users/license.rst index 77cbb4a53b83..73067028e33b 100644 --- a/doc/users/license.rst +++ b/doc/users/license.rst @@ -13,22 +13,98 @@ licenses. Non-BSD compatible licenses (eg LGPL) are acceptable in matplotlib toolkits. For a discussion of the motivations behind the licencing choice, see :ref:`license-discussion`. +Copyright Policy +================ + +John Hunter began matplotlib around 2003. Since shortly before his +passing in 2012, Michael Droettboom has been the lead maintainer of +matplotlib, but, as has always been the case, matplotlib is the work +of many. + +Prior to July of 2013, and the 1.3.0 release, the copyright of the +source code was held by John Hunter. As of July 2013, and the 1.3.0 +release, matplotlib has moved to a shared copyright model. + +matplotlib uses a shared copyright model. Each contributor maintains +copyright over their contributions to matplotlib. But, it is important to +note that these contributions are typically only changes to the +repositories. Thus, the matplotlib source code, in its entirety, is not +the copyright of any single person or institution. Instead, it is the +collective copyright of the entire matplotlib Development Team. If +individual contributors want to maintain a record of what +changes/contributions they have specific copyright on, they should +indicate their copyright in the commit message of the change, when +they commit the change to one of the IPython repositories. + +The Matplotlib Development Team is the set of all contributors to the +matplotlib project. A full list can be obtained from the git version +control logs. License agreement for matplotlib |version| ============================================== -1. This LICENSE AGREEMENT is between Michael Droettboom ("MDB"), and the +1. This LICENSE AGREEMENT is between the Matplotlib Development Team +("MDT"), and the Individual or Organization ("Licensee") accessing and +otherwise using matplotlib software in source or binary form and its +associated documentation. + +. Subject to the terms and conditions of this License Agreement, MDT +hereby grants Licensee a nonexclusive, royalty-free, world-wide license +to reproduce, analyze, test, perform and/or display publicly, prepare +derivative works, distribute, and otherwise use matplotlib |version| +alone or in any derivative version, provided, however, that MDT's +License Agreement and MDT's notice of copyright, i.e., "Copyright (c) +2012-2013 Matplotlib Development Team; All Rights Reserved" are retained in +matplotlib |version| alone or in any derivative version prepared by +Licensee. + +3. In the event Licensee prepares a derivative work that is based on or +incorporates matplotlib |version| or any part thereof, and wants to +make the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to matplotlib |version|. + +4. MDT is making matplotlib |version| available to Licensee on an "AS +IS" basis. MDT MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, MDT MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB |version| +WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +5. MDT SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB +|version| FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR +LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING +MATPLOTLIB |version|, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF +THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between MDT and +Licensee. This License Agreement does not grant permission to use MDT +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using matplotlib |version|, +Licensee agrees to be bound by the terms and conditions of this License +Agreement. + +License agreement for matplotlib versions prior to 1.3.0 +======================================================== + +1. This LICENSE AGREEMENT is between John D. Hunter ("JDH"), and the Individual or Organization ("Licensee") accessing and otherwise using matplotlib software in source or binary form and its associated documentation. -2. Subject to the terms and conditions of this License Agreement, MDB +2. Subject to the terms and conditions of this License Agreement, JDH hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use matplotlib |version| -alone or in any derivative version, provided, however, that MDB's -License Agreement and MDB's notice of copyright, i.e., "Copyright (c) -2002-2013 Michael Droettboom; All Rights Reserved" are retained in +alone or in any derivative version, provided, however, that JDH's +License Agreement and JDH's notice of copyright, i.e., "Copyright (c) +2002-2009 John D. Hunter; All Rights Reserved" are retained in matplotlib |version| alone or in any derivative version prepared by Licensee. @@ -38,14 +114,14 @@ make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to matplotlib |version|. -4. MDB is making matplotlib |version| available to Licensee on an "AS -IS" basis. MDB MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, MDB MAKES NO AND +4. JDH is making matplotlib |version| available to Licensee on an "AS +IS" basis. JDH MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, JDH MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB |version| WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. -5. MDB SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB +5. JDH SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB |version| FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING MATPLOTLIB |version|, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF @@ -55,8 +131,8 @@ THE POSSIBILITY THEREOF. breach of its terms and conditions. 7. Nothing in this License Agreement shall be deemed to create any -relationship of agency, partnership, or joint venture between MDB and -Licensee. This License Agreement does not grant permission to use MDB +relationship of agency, partnership, or joint venture between JDH and +Licensee. This License Agreement does not grant permission to use JDH trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. From e1662cb92a75baff1cb977c9fd71dc032d79ecbc Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Wed, 3 Jul 2013 15:17:03 -0400 Subject: [PATCH 16/25] Fix missed search-and-replace --- doc/users/license.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/users/license.rst b/doc/users/license.rst index 73067028e33b..073bf6a3bc33 100644 --- a/doc/users/license.rst +++ b/doc/users/license.rst @@ -34,7 +34,7 @@ collective copyright of the entire matplotlib Development Team. If individual contributors want to maintain a record of what changes/contributions they have specific copyright on, they should indicate their copyright in the commit message of the change, when -they commit the change to one of the IPython repositories. +they commit the change to one of the matplotlib repositories. The Matplotlib Development Team is the set of all contributors to the matplotlib project. A full list can be obtained from the git version From 5e5cf0cba54ab7fcf05485a707be1a4d7447247f Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Tue, 16 Jul 2013 17:23:50 -0400 Subject: [PATCH 17/25] Improve message about file in old place --- lib/matplotlib/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index ab7ffe4ee7b1..9c8baa90bbe2 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -718,11 +718,11 @@ def matplotlib_fname(): fname == os.path.join( get_home(), '.matplotlib', 'matplotlibrc')): warnings.warn( - "Found matplotlib configuration in ~/.matplotlib. " + "Found matplotlib configuration in ~/.matplotlib/. " "To conform with the XDG base directory standard, " "this configuration location has been deprecated " - "on Linux, and the new location is now %r. Please " - "move your configuration there to ensure that " + "on Linux, and the new location is now %r/matplotlib/. " + "Please move your configuration there to ensure that " "matplotlib will continue to find it in the future." % _get_xdg_config_dir()) return fname From f1e8f6e10b228e9d39eb004be339f452dbc67d2d Mon Sep 17 00:00:00 2001 From: Manuel GOACOLOU Date: Wed, 18 Sep 2013 00:22:06 +0200 Subject: [PATCH 18/25] add rotation capabilities for scatter plot and PathCollection --- .../pylab_examples/scatter_rotate_symbol.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 examples/pylab_examples/scatter_rotate_symbol.py diff --git a/examples/pylab_examples/scatter_rotate_symbol.py b/examples/pylab_examples/scatter_rotate_symbol.py new file mode 100644 index 000000000000..845d5194c8f4 --- /dev/null +++ b/examples/pylab_examples/scatter_rotate_symbol.py @@ -0,0 +1,17 @@ +import matplotlib.pyplot as plt +from numpy import arange, pi, cos, sin +from numpy.random import rand + +# unit area ellipse +rx, ry = 3., 1. +area = rx * ry * pi +theta = arange(0, 2*pi+0.01, 0.1) +verts = list(zip(rx/area*cos(theta), ry/area*sin(theta))) + +x,y,s,c = rand(4, 30) +s*= 10**2. + +fig, ax = plt.subplots() +ax.scatter(x,y,s,c,marker=None,verts =verts) + +plt.show() From 4ee760054b2c19bd6a8873536acf48d4bb39feac Mon Sep 17 00:00:00 2001 From: Manuel GOACOLOU Date: Wed, 18 Sep 2013 00:24:09 +0200 Subject: [PATCH 19/25] add rotation capabilities for scatter plot and PathCollection --- .../pylab_examples/scatter_rotate_symbol.py | 14 +++---- lib/matplotlib/axes/_axes.py | 11 ++++-- lib/matplotlib/collections.py | 37 +++++++++++++++++-- lib/matplotlib/pyplot.py | 4 +- 4 files changed, 50 insertions(+), 16 deletions(-) diff --git a/examples/pylab_examples/scatter_rotate_symbol.py b/examples/pylab_examples/scatter_rotate_symbol.py index 845d5194c8f4..ed991da07d9b 100644 --- a/examples/pylab_examples/scatter_rotate_symbol.py +++ b/examples/pylab_examples/scatter_rotate_symbol.py @@ -1,17 +1,17 @@ import matplotlib.pyplot as plt -from numpy import arange, pi, cos, sin +from numpy import arange, pi, rad2deg from numpy.random import rand +from matplotlib.markers import TICKRIGHT -# unit area ellipse rx, ry = 3., 1. area = rx * ry * pi -theta = arange(0, 2*pi+0.01, 0.1) -verts = list(zip(rx/area*cos(theta), ry/area*sin(theta))) +theta = rad2deg(arange(0, 2*pi+0.01, 0.1)) -x,y,s,c = rand(4, 30) -s*= 10**2. + +x, y, s, c = rand(4, 30) +s *= 20**2. fig, ax = plt.subplots() -ax.scatter(x,y,s,c,marker=None,verts =verts) +ax.scatter(x, y, s, c, marker=TICKRIGHT, a=theta) plt.show() diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index e72c0ba38bd7..e6604a895e3d 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -3134,7 +3134,7 @@ def dopatch(xs, ys): medians=medians, fliers=fliers) @docstring.dedent_interpd - def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None, + def scatter(self, x, y, s=20, c='b', marker='o', a=0, cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, verts=None, **kwargs): """ @@ -3162,6 +3162,9 @@ def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None, See `~matplotlib.markers` for more information on the different styles of markers scatter supports. + a : calar or array_like, shape (n, ), optional, default: 0 degrees CCW + from X axis + cmap : `~matplotlib.colors.Colormap`, optional, default: None A `~matplotlib.colors.Colormap` instance or registered name. `cmap` is only used if `c` is an array of floats. If None, @@ -3222,6 +3225,7 @@ def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None, raise ValueError("x and y must be the same size") s = np.ma.ravel(s) # This doesn't have to match x, y in size. + a = np.ma.ravel(a) # This doesn't have to match x, y in size. c_is_stringy = is_string_like(c) or is_sequence_of_strings(c) if not c_is_stringy: @@ -3229,9 +3233,10 @@ def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None, if c.size == x.size: c = np.ma.ravel(c) - x, y, s, c = cbook.delete_masked_points(x, y, s, c) + x, y, s, c, a = cbook.delete_masked_points(x, y, s, c, a) scales = s # Renamed for readability below. + angles = a # Renamed for readability below. if c_is_stringy: colors = mcolors.colorConverter.to_rgba_array(c, alpha) @@ -3265,7 +3270,7 @@ def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None, edgecolors = 'face' collection = mcoll.PathCollection( - (path,), scales, + (path,), scales, angles, facecolors=colors, edgecolors=edgecolors, linewidths=linewidths, diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index a40546da2770..c3238b066e57 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -687,7 +687,7 @@ class PathCollection(Collection): This is the most basic :class:`Collection` subclass. """ @docstring.dedent_interpd - def __init__(self, paths, sizes=None, **kwargs): + def __init__(self, paths, sizes=None, angles=None, **kwargs): """ *paths* is a sequence of :class:`matplotlib.path.Path` instances. @@ -698,6 +698,20 @@ def __init__(self, paths, sizes=None, **kwargs): Collection.__init__(self, **kwargs) self.set_paths(paths) self._sizes = sizes + self._angles = angles + self.__check_parameters() + + def __check_parameters(self): + if self._sizes is not None and self._angles is not None and self._sizes.size != self._angles.size: + ar_resize = np.resize + if isinstance(self._sizes, np.ma.core.MaskedArray): + ar_resize = np.ma.resize + + # make same size + if self._sizes.size > self._angles.size: + self._angles = ar_resize(self._angles, self._sizes.shape) + else: + self._sizes = ar_resize(self._sizes, self._angles.shape) def set_paths(self, paths): self._paths = paths @@ -708,13 +722,28 @@ def get_paths(self): def get_sizes(self): return self._sizes + def get_angles(self): + return self._angles + @allow_rasterization def draw(self, renderer): - if self._sizes is not None: + if self._sizes is not None and self._angles is not None: + if self._sizes.size != self._angles.size: + raise ValueError("sizes and angles must have the same size") self._transforms = [ transforms.Affine2D().scale( - (np.sqrt(x) * self.figure.dpi / 72.0)) - for x in self._sizes] + (np.sqrt(s) * self.figure.dpi / 72.0)).rotate(a) + for s, a in zip(self._sizes, self._angles)] + elif self._sizes is not None: + self._transforms = [ + transforms.Affine2D().scale( + (np.sqrt(s) * self.figure.dpi / 72.0)) + for s in self._sizes] + elif self._angles is not None: + self._transforms = [ + transforms.Affine2D().rotate(a) + for a in self._angles] + return Collection.draw(self, renderer) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index ed8e157ba3c0..422efc4eadd3 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -3075,7 +3075,7 @@ def quiverkey(*args, **kw): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.scatter) -def scatter(x, y, s=20, c='b', marker='o', cmap=None, norm=None, vmin=None, +def scatter(x, y, s=20, c='b', marker='o', a=0, cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, verts=None, hold=None, **kwargs): ax = gca() @@ -3085,7 +3085,7 @@ def scatter(x, y, s=20, c='b', marker='o', cmap=None, norm=None, vmin=None, if hold is not None: ax.hold(hold) try: - ret = ax.scatter(x, y, s=s, c=c, marker=marker, cmap=cmap, norm=norm, + ret = ax.scatter(x, y, s=s, c=c, marker=marker, a=a, cmap=cmap, norm=norm, vmin=vmin, vmax=vmax, alpha=alpha, linewidths=linewidths, verts=verts, **kwargs) draw_if_interactive() From c5239ea6c1e4250584b58ee9c624ff25fda56527 Mon Sep 17 00:00:00 2001 From: Manuel GOACOLOU Date: Wed, 18 Sep 2013 10:25:14 +0200 Subject: [PATCH 20/25] PEP8 line too long --- lib/matplotlib/collections.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index c3238b066e57..905d0a6a152d 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -702,7 +702,10 @@ def __init__(self, paths, sizes=None, angles=None, **kwargs): self.__check_parameters() def __check_parameters(self): - if self._sizes is not None and self._angles is not None and self._sizes.size != self._angles.size: + if self._sizes is not None \ + and self._angles is not None \ + and self._sizes.size != self._angles.size: + ar_resize = np.resize if isinstance(self._sizes, np.ma.core.MaskedArray): ar_resize = np.ma.resize From 11dbc2068ab5da430d86646144c2c689d4eaad12 Mon Sep 17 00:00:00 2001 From: Manuel GOACOLOU Date: Wed, 18 Sep 2013 10:47:16 +0200 Subject: [PATCH 21/25] PathCollection docstring and code revue from pelson --- lib/matplotlib/collections.py | 42 ++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 905d0a6a152d..0d5592b21866 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -692,6 +692,10 @@ def __init__(self, paths, sizes=None, angles=None, **kwargs): *paths* is a sequence of :class:`matplotlib.path.Path` instances. + *sizes* is an array of length 1 or more + + *angless* is an array of length 1 or more + %(Collection)s """ @@ -699,9 +703,13 @@ def __init__(self, paths, sizes=None, angles=None, **kwargs): self.set_paths(paths) self._sizes = sizes self._angles = angles - self.__check_parameters() + self._check_parameters() - def __check_parameters(self): + def _check_parameters(self): + """ + Check the sizes and array dimention to make them the same size + Needed for drawing them efficiently + """ if self._sizes is not None \ and self._angles is not None \ and self._sizes.size != self._angles.size: @@ -710,29 +718,53 @@ def __check_parameters(self): if isinstance(self._sizes, np.ma.core.MaskedArray): ar_resize = np.ma.resize - # make same size + # Make sizes array and angles array same size if self._sizes.size > self._angles.size: self._angles = ar_resize(self._angles, self._sizes.shape) else: self._sizes = ar_resize(self._sizes, self._angles.shape) def set_paths(self, paths): + """ + update the paths sequence + """ self._paths = paths def get_paths(self): + """ + return the paths sequence + """ return self._paths + def set_sizes(self, sizes): + """ + update sizes array check array size + """ + self._sizes = sizes + self._check_parameters() + def get_sizes(self): + """ + return the sizes array + """ return self._sizes + def set_angles(self, angles): + """ + update angles array check array size + """ + self._angles = angles + self._check_parameters() + def get_angles(self): + """ + return the angle array + """ return self._angles @allow_rasterization def draw(self, renderer): if self._sizes is not None and self._angles is not None: - if self._sizes.size != self._angles.size: - raise ValueError("sizes and angles must have the same size") self._transforms = [ transforms.Affine2D().scale( (np.sqrt(s) * self.figure.dpi / 72.0)).rotate(a) From 9fb25cc16c6e9b3424dca5bf159ed5fa8117cfe9 Mon Sep 17 00:00:00 2001 From: Manuel GOACOLOU Date: Wed, 18 Sep 2013 10:54:49 +0200 Subject: [PATCH 22/25] Axes.scatter and pyplot.scatter updates with code revue from pelson --- lib/matplotlib/axes/_axes.py | 11 +++++------ lib/matplotlib/pyplot.py | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index e6604a895e3d..a26425fceb1e 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -3134,7 +3134,7 @@ def dopatch(xs, ys): medians=medians, fliers=fliers) @docstring.dedent_interpd - def scatter(self, x, y, s=20, c='b', marker='o', a=0, cmap=None, norm=None, + def scatter(self, x, y, s=20, c='b', marker='o', angles=0, cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, verts=None, **kwargs): """ @@ -3162,8 +3162,8 @@ def scatter(self, x, y, s=20, c='b', marker='o', a=0, cmap=None, norm=None, See `~matplotlib.markers` for more information on the different styles of markers scatter supports. - a : calar or array_like, shape (n, ), optional, default: 0 degrees CCW - from X axis + angles : scalar or array_like, shape (n, ), optional, default: 0 + degrees CCW from X axis cmap : `~matplotlib.colors.Colormap`, optional, default: None A `~matplotlib.colors.Colormap` instance or registered name. @@ -3225,7 +3225,7 @@ def scatter(self, x, y, s=20, c='b', marker='o', a=0, cmap=None, norm=None, raise ValueError("x and y must be the same size") s = np.ma.ravel(s) # This doesn't have to match x, y in size. - a = np.ma.ravel(a) # This doesn't have to match x, y in size. + angles = np.ma.ravel(angles) # This doesn't have to match x, y in size. c_is_stringy = is_string_like(c) or is_sequence_of_strings(c) if not c_is_stringy: @@ -3233,10 +3233,9 @@ def scatter(self, x, y, s=20, c='b', marker='o', a=0, cmap=None, norm=None, if c.size == x.size: c = np.ma.ravel(c) - x, y, s, c, a = cbook.delete_masked_points(x, y, s, c, a) + x, y, s, c, angles = cbook.delete_masked_points(x, y, s, c, angles) scales = s # Renamed for readability below. - angles = a # Renamed for readability below. if c_is_stringy: colors = mcolors.colorConverter.to_rgba_array(c, alpha) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 422efc4eadd3..20b7fa3867fc 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -3075,7 +3075,7 @@ def quiverkey(*args, **kw): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.scatter) -def scatter(x, y, s=20, c='b', marker='o', a=0, cmap=None, norm=None, vmin=None, +def scatter(x, y, s=20, c='b', marker='o', angles=0, cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, verts=None, hold=None, **kwargs): ax = gca() @@ -3085,7 +3085,7 @@ def scatter(x, y, s=20, c='b', marker='o', a=0, cmap=None, norm=None, vmin=None, if hold is not None: ax.hold(hold) try: - ret = ax.scatter(x, y, s=s, c=c, marker=marker, a=a, cmap=cmap, norm=norm, + ret = ax.scatter(x, y, s=s, c=c, marker=marker, angles=angles, cmap=cmap, norm=norm, vmin=vmin, vmax=vmax, alpha=alpha, linewidths=linewidths, verts=verts, **kwargs) draw_if_interactive() From 9abda5a10e9432e8bc68fd43968c45dff8219fad Mon Sep 17 00:00:00 2001 From: Manuel GOACOLOU Date: Wed, 18 Sep 2013 14:38:08 +0200 Subject: [PATCH 23/25] update example --- examples/pylab_examples/scatter_rotate_symbol.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/examples/pylab_examples/scatter_rotate_symbol.py b/examples/pylab_examples/scatter_rotate_symbol.py index ed991da07d9b..02adc62a0fc2 100644 --- a/examples/pylab_examples/scatter_rotate_symbol.py +++ b/examples/pylab_examples/scatter_rotate_symbol.py @@ -1,17 +1,15 @@ +import numpy as np import matplotlib.pyplot as plt -from numpy import arange, pi, rad2deg -from numpy.random import rand from matplotlib.markers import TICKRIGHT rx, ry = 3., 1. -area = rx * ry * pi -theta = rad2deg(arange(0, 2*pi+0.01, 0.1)) +area = rx * ry * np.pi +angles = np.linspace(0., 360., 30.) +x, y, sizes, colors = np.random.rand(4, 30) +sizes *= 20**2. -x, y, s, c = rand(4, 30) -s *= 20**2. - -fig, ax = plt.subplots() -ax.scatter(x, y, s, c, marker=TICKRIGHT, a=theta) +plt.scatter(x, y, sizes, colors, marker="o",zorder=2) +plt.scatter(x, y, 2.5*sizes, colors, marker=TICKRIGHT, angles=angles, zorder=2) plt.show() From b084c4402541910130f5457b0cb7ab37ad5be818 Mon Sep 17 00:00:00 2001 From: Manuel GOACOLOU Date: Wed, 18 Sep 2013 22:45:25 +0200 Subject: [PATCH 24/25] update pyplots with boilerplate.py --- lib/matplotlib/pyplot.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 20b7fa3867fc..cddfe9c5f8f9 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -3075,9 +3075,9 @@ def quiverkey(*args, **kw): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.scatter) -def scatter(x, y, s=20, c='b', marker='o', angles=0, cmap=None, norm=None, vmin=None, - vmax=None, alpha=None, linewidths=None, verts=None, hold=None, - **kwargs): +def scatter(x, y, s=20, c='b', marker='o', angles=0, cmap=None, norm=None, + vmin=None, vmax=None, alpha=None, linewidths=None, verts=None, + hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -3085,9 +3085,10 @@ def scatter(x, y, s=20, c='b', marker='o', angles=0, cmap=None, norm=None, vmin= if hold is not None: ax.hold(hold) try: - ret = ax.scatter(x, y, s=s, c=c, marker=marker, angles=angles, cmap=cmap, norm=norm, - vmin=vmin, vmax=vmax, alpha=alpha, - linewidths=linewidths, verts=verts, **kwargs) + ret = ax.scatter(x, y, s=s, c=c, marker=marker, angles=angles, + cmap=cmap, norm=norm, vmin=vmin, vmax=vmax, + alpha=alpha, linewidths=linewidths, verts=verts, + **kwargs) draw_if_interactive() finally: ax.hold(washold) @@ -3213,7 +3214,7 @@ def step(x, y, *args, **kwargs): @_autogen_docstring(Axes.streamplot) def streamplot(x, y, u, v, density=1, linewidth=None, color=None, cmap=None, norm=None, arrowsize=1, arrowstyle='-|>', minlength=0.1, - transform=None, hold=None, zorder=1): + transform=None, zorder=1, hold=None): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() From 3c9c26241280f625350cbbcb79956c22fdc32938 Mon Sep 17 00:00:00 2001 From: Manuel GOACOLOU Date: Thu, 19 Sep 2013 00:30:31 +0200 Subject: [PATCH 25/25] refactor and improvement from WheatherGod and NellV revue --- lib/matplotlib/axes/_axes.py | 2 +- lib/matplotlib/collections.py | 43 +++++++++++++++++------------------ 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index a26425fceb1e..100850224b00 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -3163,7 +3163,7 @@ def scatter(self, x, y, s=20, c='b', marker='o', angles=0, cmap=None, norm=None, styles of markers scatter supports. angles : scalar or array_like, shape (n, ), optional, default: 0 - degrees CCW from X axis + degrees counter clock-wise from X axis cmap : `~matplotlib.colors.Colormap`, optional, default: None A `~matplotlib.colors.Colormap` instance or registered name. diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 0d5592b21866..eb5ac7f5ef99 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -692,9 +692,10 @@ def __init__(self, paths, sizes=None, angles=None, **kwargs): *paths* is a sequence of :class:`matplotlib.path.Path` instances. - *sizes* is an array of length 1 or more + *sizes* is an array of length 1 or more, size in points^2 - *angless* is an array of length 1 or more + *angles* is an array of length 1 or more, degrees + counter clock-wise from X axis %(Collection)s """ @@ -703,26 +704,22 @@ def __init__(self, paths, sizes=None, angles=None, **kwargs): self.set_paths(paths) self._sizes = sizes self._angles = angles - self._check_parameters() + self._normalize_parameters() - def _check_parameters(self): + def _normalize_parameters(self): """ Check the sizes and array dimention to make them the same size Needed for drawing them efficiently """ - if self._sizes is not None \ - and self._angles is not None \ - and self._sizes.size != self._angles.size: - - ar_resize = np.resize - if isinstance(self._sizes, np.ma.core.MaskedArray): - ar_resize = np.ma.resize + if (self._sizes is not None + and self._angles is not None + and self._sizes.size != self._angles.size): # Make sizes array and angles array same size if self._sizes.size > self._angles.size: - self._angles = ar_resize(self._angles, self._sizes.shape) + self._angles = np.ma.resize(self._angles, self._sizes.shape) else: - self._sizes = ar_resize(self._sizes, self._angles.shape) + self._sizes = np.ma.resize(self._sizes, self._angles.shape) def set_paths(self, paths): """ @@ -741,7 +738,7 @@ def set_sizes(self, sizes): update sizes array check array size """ self._sizes = sizes - self._check_parameters() + self._normalize_parameters() def get_sizes(self): """ @@ -754,7 +751,7 @@ def set_angles(self, angles): update angles array check array size """ self._angles = angles - self._check_parameters() + self._normalize_parameters() def get_angles(self): """ @@ -765,19 +762,21 @@ def get_angles(self): @allow_rasterization def draw(self, renderer): if self._sizes is not None and self._angles is not None: + _sizes = np.sqrt(self._sizes) * self.figure.dpi / 72.0 + _angles = np.deg2rad(self._angles) self._transforms = [ - transforms.Affine2D().scale( - (np.sqrt(s) * self.figure.dpi / 72.0)).rotate(a) - for s, a in zip(self._sizes, self._angles)] + transforms.Affine2D().scale(s).rotate(a) + for s, a in zip(_sizes, _angles)] elif self._sizes is not None: + _sizes = np.sqrt(self._sizes) * self.figure.dpi / 72.0 self._transforms = [ - transforms.Affine2D().scale( - (np.sqrt(s) * self.figure.dpi / 72.0)) - for s in self._sizes] + transforms.Affine2D().scale(s) + for s in _sizes] elif self._angles is not None: + _angles = np.deg2rad(self._angles) self._transforms = [ transforms.Affine2D().rotate(a) - for a in self._angles] + for a in _angles] return Collection.draw(self, renderer)