diff --git a/examples/pylab_examples/fancyarrow_demo.py b/examples/pylab_examples/fancyarrow_demo.py index 6fceb9878d18..227e53bb36e0 100644 --- a/examples/pylab_examples/fancyarrow_demo.py +++ b/examples/pylab_examples/fancyarrow_demo.py @@ -25,7 +25,7 @@ def to_texstring(s): for i, (stylename, styleclass) in enumerate(sorted(styles.items())): x = 3.2 + (i//nrow)*4 y = (figheight - 0.7 - i % nrow) # /figheight - p = mpatches.Circle((x, y), 0.2, fc="w") + p = mpatches.Circle((x, y), 0.2) ax.add_patch(p) ax.annotate(to_texstring(stylename), (x, y), @@ -37,7 +37,7 @@ def to_texstring(s): patchB=p, shrinkA=5, shrinkB=5, - fc="w", ec="k", + fc="k", ec="k", connectionstyle="arc3,rad=-0.05", ), bbox=dict(boxstyle="square", fc="w")) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 76c9f3cce7fb..16a249e0d6e8 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -1986,7 +1986,7 @@ def bar(self, left, height, width=0.8, bottom=None, **kwargs): xerr = kwargs.pop('xerr', None) yerr = kwargs.pop('yerr', None) error_kw = kwargs.pop('error_kw', dict()) - ecolor = kwargs.pop('ecolor', None) + ecolor = kwargs.pop('ecolor', 'k') capsize = kwargs.pop('capsize', rcParams["errorbar.capsize"]) error_kw.setdefault('ecolor', ecolor) error_kw.setdefault('capsize', capsize) @@ -3762,11 +3762,16 @@ def scatter(self, x, y, s=20, c=None, marker='o', cmap=None, norm=None, If None, defaults to (lines.linewidth,). edgecolors : color or sequence of color, optional, default: None - If None, defaults to (patch.edgecolor). + If None, defaults to 'face' + If 'face', the edge color will always be the same as - the face color. If it is 'none', the patch boundary will not - be drawn. For non-filled markers, the `edgecolors` kwarg - is ignored; color is determined by `c`. + the face color. + + If it is 'none', the patch boundary will not + be drawn. + + For non-filled markers, the `edgecolors` kwarg + is ignored and forced to 'face' internally. Returns ------- @@ -3823,6 +3828,9 @@ def scatter(self, x, y, s=20, c=None, marker='o', cmap=None, norm=None, else: c = 'b' # The original default + if edgecolors is None and not rcParams['_internal.classic_mode']: + edgecolors = 'face' + self._process_unit_info(xdata=x, ydata=y, kwargs=kwargs) x = self.convert_xunits(x) y = self.convert_yunits(y) @@ -3875,6 +3883,7 @@ def scatter(self, x, y, s=20, c=None, marker='o', cmap=None, norm=None, marker_obj.get_transform()) if not marker_obj.is_filled(): edgecolors = 'face' + linewidths = rcParams['lines.linewidth'] offsets = np.dstack((x, y)) @@ -4018,9 +4027,9 @@ def hexbin(self, x, y, C=None, gridsize=100, bins=None, the alpha value for the patches *linewidths*: [ *None* | scalar ] - If *None*, defaults to rc lines.linewidth. Note that this - is a tuple, and if you set the linewidths argument you - must set it as a sequence of floats, as required by + If *None*, defaults to 1.0. Note that this is a tuple, and + if you set the linewidths argument you must set it as a + sequence of floats, as required by :class:`~matplotlib.collections.RegularPolyCollection`. Other keyword arguments controlling the Collection properties: @@ -4213,6 +4222,8 @@ def hexbin(self, x, y, C=None, gridsize=100, bins=None, if edgecolors == 'none': edgecolors = 'face' + if linewidths is None: + linewidths = [1.0] if xscale == 'log' or yscale == 'log': polygons = np.expand_dims(polygon, 0) + np.expand_dims(offsets, 1) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 41afd14cce60..dcfac21ada23 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -325,7 +325,7 @@ def _makefill(self, x, y, kw, kwargs): seg = mpatches.Polygon(np.hstack((x[:, np.newaxis], y[:, np.newaxis])), facecolor=facecolor, - fill=True, + fill=kwargs.get('fill', True), closed=kw['closed']) self.set_patchprops(seg, **kwargs) return seg diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 58e4d955eeb8..7032bce22a21 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -88,6 +88,10 @@ class Collection(artist.Artist, cm.ScalarMappable): #: Each kind of collection defines this based on its arguments. _transforms = np.empty((0, 3, 3)) + # Whether to draw an edge by default. Set on a + # subclass-by-subclass basis. + _edge_default = False + def __init__(self, edgecolors=None, facecolors=None, @@ -476,7 +480,15 @@ def set_linewidth(self, lw): ACCEPTS: float or sequence of floats """ if lw is None: - lw = mpl.rcParams['patch.linewidth'] + if (self._edge_default or + mpl.rcParams['_internal.classic_mode'] or + not self._is_filled): + lw = mpl.rcParams['patch.linewidth'] + if lw is None: + lw = mpl.rcParams['lines.linewidth'] + else: + lw = 0 + self._linewidths = self._get_value(lw) self.stale = True @@ -1046,6 +1058,8 @@ class LineCollection(Collection): number of segments. """ + _edge_default = True + def __init__(self, segments, # Can be None. linewidths=None, colors=None, @@ -1217,6 +1231,8 @@ class EventCollection(LineCollection): are displayed as v ''' + _edge_default = True + def __init__(self, positions, # Can be None. orientation=None, diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index f8aada39b927..cba5d6f04b84 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -849,12 +849,12 @@ def get_marker(self): def get_markeredgecolor(self): mec = self._markeredgecolor if (is_string_like(mec) and mec == 'auto'): - if self._marker.get_marker() in ('.', ','): - return self._color - if self._marker.is_filled() and self.get_fillstyle() != 'none': - return 'k' # Bad hard-wired default... - else: - return self._color + if rcParams['_internal.classic_mode']: + if self._marker.get_marker() in ('.', ','): + return self._color + if self._marker.is_filled() and self.get_fillstyle() != 'none': + return 'k' # Bad hard-wired default... + return self._color else: return mec diff --git a/lib/matplotlib/mpl-data/stylelib/classic.mplstyle b/lib/matplotlib/mpl-data/stylelib/classic.mplstyle index e91dfae05d61..7660f8575e9c 100644 --- a/lib/matplotlib/mpl-data/stylelib/classic.mplstyle +++ b/lib/matplotlib/mpl-data/stylelib/classic.mplstyle @@ -497,3 +497,5 @@ animation.convert_path: convert # Path to ImageMagick's convert binary. # is also the name of a system tool. animation.convert_args: animation.html: none + +_internal.classic_mode: True \ No newline at end of file diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py index c58958ef64db..addac4992be4 100644 --- a/lib/matplotlib/patches.py +++ b/lib/matplotlib/patches.py @@ -67,6 +67,10 @@ class Patch(artist.Artist): validCap = ('butt', 'round', 'projecting') validJoin = ('miter', 'round', 'bevel') + # Whether to draw an edge by default. Set on a + # subclass-by-subclass basis. + _edge_default = False + def __str__(self): return str(self.__class__).split('.')[-1] @@ -110,11 +114,12 @@ def __init__(self, else: self.set_edgecolor(edgecolor) self.set_facecolor(facecolor) + + self.set_fill(fill) self.set_linewidth(linewidth) self.set_linestyle(linestyle) self.set_antialiased(antialiased) self.set_hatch(hatch) - self.set_fill(fill) self.set_capstyle(capstyle) self.set_joinstyle(joinstyle) self._combined_transform = transforms.IdentityTransform() @@ -339,7 +344,14 @@ def set_linewidth(self, w): ACCEPTS: float or None for default """ if w is None: - w = mpl.rcParams['patch.linewidth'] + if (not self._fill or + self._edge_default or + mpl.rcParams['_internal.classic_mode']): + w = mpl.rcParams['patch.linewidth'] + if w is None: + w = mpl.rcParams['axes.linewidth'] + else: + w = 0 self._linewidth = float(w) @@ -848,6 +860,8 @@ class PathPatch(Patch): """ A general polycurve path patch. """ + _edge_default = True + def __str__(self): return "Poly((%g, %g) ...)" % tuple(self._path.vertices[0]) @@ -1120,6 +1134,8 @@ class FancyArrow(Polygon): Like Arrow, but lets you set head width and head height independently. """ + _edge_default = True + def __str__(self): return "FancyArrow()" @@ -2397,6 +2413,8 @@ class FancyBboxPatch(Patch): """ + _edge_default = True + def __str__(self): return self.__class__.__name__ \ + "(%g,%g;%gx%g)" % (self._x, self._y, @@ -2449,6 +2467,7 @@ def __init__(self, xy, width, height, self._mutation_scale = mutation_scale self._mutation_aspect = mutation_aspect + self.stale = True @docstring.dedent_interpd @@ -3935,6 +3954,7 @@ class FancyArrowPatch(Patch): """ A fancy arrow patch. It draws an arrow using the :class:ArrowStyle. """ + _edge_default = True def __str__(self): diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index f52e8305f335..70c693563e64 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -813,7 +813,7 @@ def validate_hist_bins(s): 'lines.linestyle': ['-', six.text_type], # solid line 'lines.color': ['b', validate_color], # blue 'lines.marker': ['None', six.text_type], # black - 'lines.markeredgewidth': [0.5, validate_float], + 'lines.markeredgewidth': [1.0, validate_float], 'lines.markersize': [6, validate_float], # markersize, in points 'lines.antialiased': [True, validate_bool], # antialiased (no jaggies) 'lines.dash_joinstyle': ['round', validate_joinstyle], @@ -825,7 +825,7 @@ def validate_hist_bins(s): 'markers.fillstyle': ['full', validate_fillstyle], ## patch props - 'patch.linewidth': [1.0, validate_float], # line width in points + 'patch.linewidth': [None, validate_float_or_None], # line width in points 'patch.edgecolor': ['k', validate_color], # black 'patch.facecolor': ['b', validate_color], # blue 'patch.antialiased': [True, validate_bool], # antialiased (no jaggies) @@ -1035,7 +1035,7 @@ def validate_hist_bins(s): 'legend.markerscale': [1.0, validate_float], 'legend.shadow': [False, validate_bool], 'legend.facecolor': ['inherit', validate_color_or_inherit], - 'legend.edgecolor': ['inherit', validate_color_or_inherit], + 'legend.edgecolor': ['k', validate_color_or_inherit], # tick properties 'xtick.top': [True, validate_bool], # draw ticks on the top side @@ -1208,7 +1208,14 @@ def validate_hist_bins(s): 'animation.convert_path': ['convert', six.text_type], # Additional arguments for mencoder movie writer (using pipes) - 'animation.convert_args': [[], validate_stringlist]} + 'animation.convert_args': [[], validate_stringlist], + + # Classic (pre 2.0) compatibility mode + # This is used for things that are hard to make backward compatible + # with a sane rcParam alone. This does *not* turn on classic mode + # altogether. For that use `matplotlib.style.use('classic')`. + '_internal.classic_mode': [False, validate_bool] +} if __name__ == '__main__': diff --git a/lib/matplotlib/tests/baseline_images/test_artist/default_edges.png b/lib/matplotlib/tests/baseline_images/test_artist/default_edges.png new file mode 100644 index 000000000000..932e2d9ec08b Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_artist/default_edges.png differ diff --git a/lib/matplotlib/tests/test_artist.py b/lib/matplotlib/tests/test_artist.py index 4f8fe30c9253..6757158d2a27 100644 --- a/lib/matplotlib/tests/test_artist.py +++ b/lib/matplotlib/tests/test_artist.py @@ -12,6 +12,7 @@ import matplotlib.path as mpath import matplotlib.transforms as mtrans import matplotlib.collections as mcollections +import matplotlib as mpl from matplotlib.testing.decorators import image_comparison, cleanup from nose.tools import (assert_true, assert_false) @@ -176,6 +177,26 @@ def test_remove(): assert_true(ax.stale) +@image_comparison(baseline_images=["default_edges"], remove_text=True, + extensions=['png'], style='default') +def test_default_edges(): + with mpl.rc_context({'patch.linewidth': None}): + fig, [[ax1, ax2], [ax3, ax4]] = plt.subplots(2, 2) + + ax1.plot(np.arange(10), np.arange(10), 'x', + np.arange(10) + 1, np.arange(10), 'o') + ax2.bar(np.arange(10), np.arange(10)) + ax3.text(0, 0, "BOX", size=24, bbox=dict(boxstyle='sawtooth')) + ax3.set_xlim((-1, 1)) + ax3.set_ylim((-1, 1)) + pp1 = mpatches.PathPatch( + mpath.Path([(0, 0), (1, 0), (1, 1), (0, 0)], + [mpath.Path.MOVETO, mpath.Path.CURVE3, + mpath.Path.CURVE3, mpath.Path.CLOSEPOLY]), + fc="none", transform=ax4.transData) + ax4.add_patch(pp1) + + if __name__ == '__main__': import nose nose.runmodule(argv=['-s', '--with-doctest'], exit=False) diff --git a/matplotlibrc.template b/matplotlibrc.template index 16c78d43967d..b4e602b846c5 100644 --- a/matplotlibrc.template +++ b/matplotlibrc.template @@ -82,7 +82,7 @@ backend : %(backend)s #lines.linestyle : - # solid line #lines.color : blue # has no affect on plot(); see axes.prop_cycle #lines.marker : None # the default marker -#lines.markeredgewidth : 0.5 # the line width around the marker symbol +#lines.markeredgewidth : 1.0 # the line width around the marker symbol #lines.markersize : 6 # markersize, in points #lines.dash_joinstyle : miter # miter|round|bevel #lines.dash_capstyle : butt # butt|round|projecting @@ -97,8 +97,10 @@ backend : %(backend)s # circles. See # http://matplotlib.org/api/artist_api.html#module-matplotlib.patches # information on patch properties -#patch.linewidth : 1.0 # edge width in points -#patch.facecolor : blue +#patch.linewidth : None # edge width in points. + # If None, use axes.linewidth when patch + # is not filled. +#patch.facecolor : b #patch.edgecolor : black #patch.antialiased : True # render patches in antialiased (no jaggies) @@ -377,6 +379,8 @@ backend : %(backend)s #legend.frameon : True # whether or not to draw a frame around legend #legend.framealpha : None # opacity of of legend frame #legend.scatterpoints : 3 # number of scatter points +#legend.facecolor : inherit +#legend.edgecolor : k ### FIGURE # See http://matplotlib.org/api/figure_api.html#matplotlib.figure.Figure