Skip to content

Commit

Permalink
Merge pull request #6904 from efiring/scatter_edgecolor
Browse files Browse the repository at this point in the history
MNT: Use edgecolor rather than linewidth to control edge display.
  • Loading branch information
tacaswell committed Aug 22, 2016
2 parents b2a25b2 + 69af0e7 commit 36f1353
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 113 deletions.
103 changes: 51 additions & 52 deletions lib/matplotlib/collections.py
Expand Up @@ -128,9 +128,10 @@ def __init__(self,
# list of unbroadcast/scaled linewidths
self._us_lw = [0]
self._linewidths = [0]
self._is_filled = True # May be modified by set_facecolor().

self.set_edgecolor(edgecolors)
self.set_facecolor(facecolors)
self.set_edgecolor(edgecolors)
self.set_linewidth(linewidths)
self.set_linestyle(linestyles)
self.set_antialiased(antialiaseds)
Expand Down Expand Up @@ -492,14 +493,9 @@ def set_linewidth(self, lw):
ACCEPTS: float or sequence of floats
"""
if lw is None:
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
lw = mpl.rcParams['patch.linewidth']
if lw is None:
lw = mpl.rcParams['lines.linewidth']
# get the un-scaled/broadcast lw
self._us_lw = self._get_value(lw)

Expand Down Expand Up @@ -644,6 +640,20 @@ def set_color(self, c):
self.set_facecolor(c)
self.set_edgecolor(c)

def _set_facecolor(self, c):
if c is None:
c = mpl.rcParams['patch.facecolor']

self._is_filled = True
try:
if c.lower() == 'none':
self._is_filled = False
except AttributeError:
pass
self._facecolors = mcolors.to_rgba_array(c, self._alpha)
self.stale = True


def set_facecolor(self, c):
"""
Set the facecolor(s) of the collection. *c* can be a
Expand All @@ -655,17 +665,9 @@ def set_facecolor(self, c):
ACCEPTS: matplotlib color spec or sequence of specs
"""
self._is_filled = True
try:
if c.lower() == 'none':
self._is_filled = False
except AttributeError:
pass
if c is None:
c = mpl.rcParams['patch.facecolor']
self._facecolors_original = c
self._facecolors = mcolors.to_rgba_array(c, self._alpha)
self.stale = True
self._original_facecolor = c
self._set_facecolor(c)


def set_facecolors(self, c):
"""alias for set_facecolor"""
Expand All @@ -683,38 +685,45 @@ def get_edgecolor(self):
return self._edgecolors
get_edgecolors = get_edgecolor

def set_edgecolor(self, c):
"""
Set the edgecolor(s) of the collection. *c* can be a
matplotlib color spec (all patches have same color), or a
sequence of specs; if it is a sequence the patches will
cycle through the sequence.
If *c* is 'face', the edge color will always be the same as
the face color. If it is 'none', the patch boundary will not
be drawn.
ACCEPTS: matplotlib color spec or sequence of specs
"""
def _set_edgecolor(self, c):
if c is None:
if (mpl.rcParams['patch.force_edgecolor'] or
not self._is_filled or self._edge_default):
c = mpl.rcParams['patch.edgecolor']
else:
c = 'none'
self._is_stroked = True
try:
if c.lower() == 'none':
self._is_stroked = False
except AttributeError:
pass

try:
if c.lower() == 'face':
if c.lower() == 'face': # Special case: lookup in "get" method.
self._edgecolors = 'face'
self._edgecolors_original = 'face'
return
except AttributeError:
pass
if c is None:
c = mpl.rcParams['patch.edgecolor']
self._edgecolors_original = c
self._edgecolors = mcolors.to_rgba_array(c, self._alpha)
self.stale = True

def set_edgecolor(self, c):
"""
Set the edgecolor(s) of the collection. *c* can be a
matplotlib color spec (all patches have same color), or a
sequence of specs; if it is a sequence the patches will
cycle through the sequence.
If *c* is 'face', the edge color will always be the same as
the face color. If it is 'none', the patch boundary will not
be drawn.
ACCEPTS: matplotlib color spec or sequence of specs
"""
self._original_edgecolor = c
self._set_edgecolor(c)

def set_edgecolors(self, c):
"""alias for set_edgecolor"""
return self.set_edgecolor(c)
Expand All @@ -732,18 +741,8 @@ def set_alpha(self, alpha):
except TypeError:
raise TypeError('alpha must be a float or None')
artist.Artist.set_alpha(self, alpha)
try:
self._facecolors = mcolors.to_rgba_array(
self._facecolors_original, self._alpha)
except (AttributeError, TypeError, IndexError):
pass
try:
if (not isinstance(self._edgecolors_original, six.string_types)
or self._edgecolors_original != str('face')):
self._edgecolors = mcolors.to_rgba_array(
self._edgecolors_original, self._alpha)
except (AttributeError, TypeError, IndexError):
pass
self._set_facecolor(self._original_facecolor)
self._set_edgecolor(self._original_edgecolor)

def get_linewidths(self):
return self._linewidths
Expand Down Expand Up @@ -779,9 +778,9 @@ def update_from(self, other):

artist.Artist.update_from(self, other)
self._antialiaseds = other._antialiaseds
self._edgecolors_original = other._edgecolors_original
self._original_edgecolor = other._original_edgecolor
self._edgecolors = other._edgecolors
self._facecolors_original = other._facecolors_original
self._original_facecolor = other._original_facecolor
self._facecolors = other._facecolors
self._linewidths = other._linewidths
self._linestyles = other._linestyles
Expand Down
1 change: 1 addition & 0 deletions lib/matplotlib/mpl-data/stylelib/classic.mplstyle
Expand Up @@ -29,6 +29,7 @@ markers.fillstyle: full
# information on patch properties
patch.linewidth : 1.0 # edge width in points
patch.facecolor : b
patch.force_edgecolor : True
patch.edgecolor : k
patch.antialiased : True # render patches in antialiased (no jaggies)

Expand Down
78 changes: 42 additions & 36 deletions lib/matplotlib/patches.py
Expand Up @@ -154,18 +154,26 @@ def get_verts(self):
return polygons[0]
return []

def _process_radius(self, radius):
if radius is not None:
return radius
if cbook.is_numlike(self._picker):
_radius = self._picker
else:
if self.get_edgecolor()[3] == 0:
_radius = 0
else:
_radius = self.get_linewidth()
return _radius

def contains(self, mouseevent, radius=None):
"""Test whether the mouse event occurred in the patch.
Returns T/F, {}
"""
if six.callable(self._contains):
return self._contains(self, mouseevent)
if radius is None:
if cbook.is_numlike(self._picker):
radius = self._picker
else:
radius = self.get_linewidth()
radius = self._process_radius(radius)
inside = self.get_path().contains_point(
(mouseevent.x, mouseevent.y), self.get_transform(), radius)
return inside, {}
Expand All @@ -175,11 +183,7 @@ def contains_point(self, point, radius=None):
Returns *True* if the given point is inside the path
(transformed with its transform attribute).
"""
if radius is None:
if cbook.is_numlike(self._picker):
radius = self._picker
else:
radius = self.get_linewidth()
radius = self._process_radius(radius)
return self.get_path().contains_point(point,
self.get_transform(),
radius)
Expand Down Expand Up @@ -281,37 +285,44 @@ def set_aa(self, aa):
"""alias for set_antialiased"""
return self.set_antialiased(aa)

def _set_edgecolor(self, color):
if color is None:
if (mpl.rcParams['patch.force_edgecolor'] or
not self._fill or self._edge_default):
color = mpl.rcParams['patch.edgecolor']
else:
color = 'none'
self._edgecolor = colors.to_rgba(color, self._alpha)
self.stale = True

def set_edgecolor(self, color):
"""
Set the patch edge color
ACCEPTS: mpl color spec, or None for default, or 'none' for no color
ACCEPTS: mpl color spec, None, 'none', or 'auto'
"""
if color is None:
color = mpl.rcParams['patch.edgecolor']
self._original_edgecolor = color
self._edgecolor = colors.to_rgba(color, self._alpha)
self.stale = True
self._set_edgecolor(color)

def set_ec(self, color):
"""alias for set_edgecolor"""
return self.set_edgecolor(color)

def _set_facecolor(self, color):
if color is None:
color = mpl.rcParams['patch.facecolor']
alpha = self._alpha if self._fill else 0
self._facecolor = colors.to_rgba(color, alpha)
self.stale = True

def set_facecolor(self, color):
"""
Set the patch face color
ACCEPTS: mpl color spec, or None for default, or 'none' for no color
"""
if color is None:
color = mpl.rcParams['patch.facecolor']
# save: otherwise changing _fill may lose alpha information
self._original_facecolor = color
self._facecolor = colors.to_rgba(color, self._alpha)
if not self._fill:
self._facecolor = list(self._facecolor)
self._facecolor[3] = 0
self.stale = True
self._set_facecolor(color)

def set_fc(self, color):
"""alias for set_facecolor"""
Expand Down Expand Up @@ -343,10 +354,9 @@ def set_alpha(self, alpha):
except TypeError:
raise TypeError('alpha must be a float or None')
artist.Artist.set_alpha(self, alpha)
# using self._fill and self._alpha
self.set_facecolor(self._original_facecolor)
self.set_edgecolor(self._original_edgecolor)
self.stale = True
self._set_facecolor(self._facecolor)
self._set_edgecolor(self._original_edgecolor)
# stale is already True

def set_linewidth(self, w):
"""
Expand All @@ -355,14 +365,9 @@ def set_linewidth(self, w):
ACCEPTS: float or None for default
"""
if w is None:
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
w = mpl.rcParams['patch.linewidth']
if w is None:
w = mpl.rcParams['axes.linewidth']

self._linewidth = float(w)
# scale the dash pattern by the linewidth
Expand Down Expand Up @@ -428,7 +433,8 @@ def set_fill(self, b):
ACCEPTS: [True | False]
"""
self._fill = bool(b)
self.set_facecolor(self._original_facecolor)
self._set_facecolor(self._original_facecolor)
self._set_edgecolor(self._original_edgecolor)
self.stale = True

def get_fill(self):
Expand Down
13 changes: 7 additions & 6 deletions lib/matplotlib/rcsetup.py
Expand Up @@ -364,7 +364,7 @@ def validate_color(s):
'return a valid color arg'
try:
if s.lower() == 'none':
return 'None'
return 'none'
except AttributeError:
pass

Expand Down Expand Up @@ -906,7 +906,7 @@ def validate_animation_writer_path(p):
'lines.linewidth': [1.5, validate_float], # line width in points
'lines.linestyle': ['-', six.text_type], # solid line
'lines.color': ['C0', validate_color], # first color in color cycle
'lines.marker': ['None', six.text_type], # black
'lines.marker': ['None', six.text_type], # marker name
'lines.markeredgewidth': [1.0, validate_float],
'lines.markersize': [6, validate_float], # markersize, in points
'lines.antialiased': [True, validate_bool], # antialiased (no jaggies)
Expand All @@ -922,10 +922,11 @@ def validate_animation_writer_path(p):
'markers.fillstyle': ['full', validate_fillstyle],

## patch props
'patch.linewidth': [None, validate_float_or_None], # line width in points
'patch.edgecolor': ['k', validate_color], # black
'patch.facecolor': ['C0', validate_color], # first color in color cycle
'patch.antialiased': [True, validate_bool], # antialiased (no jaggies)
'patch.linewidth': [1.0, validate_float], # line width in points
'patch.edgecolor': ['k', validate_color],
'patch.force_edgecolor' : [False, validate_bool],
'patch.facecolor': ['C0', validate_color], # first color in cycle
'patch.antialiased': [True, validate_bool], # antialiased (no jaggies)

## hatch props
'hatch.linewidth': [1.0, validate_float],
Expand Down
29 changes: 14 additions & 15 deletions lib/matplotlib/tests/test_artist.py
Expand Up @@ -182,21 +182,20 @@ def test_remove():
@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)
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)


@cleanup
Expand Down

0 comments on commit 36f1353

Please sign in to comment.