Skip to content

Commit 7bf436c

Browse files
committed
Merge pull request #6904 from efiring/scatter_edgecolor
MNT: Use edgecolor rather than linewidth to control edge display.
1 parent 9982e40 commit 7bf436c

File tree

6 files changed

+118
-113
lines changed

6 files changed

+118
-113
lines changed

lib/matplotlib/collections.py

Lines changed: 51 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,10 @@ def __init__(self,
130130
# list of unbroadcast/scaled linewidths
131131
self._us_lw = [0]
132132
self._linewidths = [0]
133+
self._is_filled = True # May be modified by set_facecolor().
133134

134-
self.set_edgecolor(edgecolors)
135135
self.set_facecolor(facecolors)
136+
self.set_edgecolor(edgecolors)
136137
self.set_linewidth(linewidths)
137138
self.set_linestyle(linestyles)
138139
self.set_antialiased(antialiaseds)
@@ -494,14 +495,9 @@ def set_linewidth(self, lw):
494495
ACCEPTS: float or sequence of floats
495496
"""
496497
if lw is None:
497-
if (self._edge_default or
498-
mpl.rcParams['_internal.classic_mode'] or
499-
not self._is_filled):
500-
lw = mpl.rcParams['patch.linewidth']
501-
if lw is None:
502-
lw = mpl.rcParams['lines.linewidth']
503-
else:
504-
lw = 0
498+
lw = mpl.rcParams['patch.linewidth']
499+
if lw is None:
500+
lw = mpl.rcParams['lines.linewidth']
505501
# get the un-scaled/broadcast lw
506502
self._us_lw = self._get_value(lw)
507503

@@ -646,6 +642,20 @@ def set_color(self, c):
646642
self.set_facecolor(c)
647643
self.set_edgecolor(c)
648644

645+
def _set_facecolor(self, c):
646+
if c is None:
647+
c = mpl.rcParams['patch.facecolor']
648+
649+
self._is_filled = True
650+
try:
651+
if c.lower() == 'none':
652+
self._is_filled = False
653+
except AttributeError:
654+
pass
655+
self._facecolors = mcolors.to_rgba_array(c, self._alpha)
656+
self.stale = True
657+
658+
649659
def set_facecolor(self, c):
650660
"""
651661
Set the facecolor(s) of the collection. *c* can be a
@@ -657,17 +667,9 @@ def set_facecolor(self, c):
657667
658668
ACCEPTS: matplotlib color spec or sequence of specs
659669
"""
660-
self._is_filled = True
661-
try:
662-
if c.lower() == 'none':
663-
self._is_filled = False
664-
except AttributeError:
665-
pass
666-
if c is None:
667-
c = mpl.rcParams['patch.facecolor']
668-
self._facecolors_original = c
669-
self._facecolors = mcolors.to_rgba_array(c, self._alpha)
670-
self.stale = True
670+
self._original_facecolor = c
671+
self._set_facecolor(c)
672+
671673

672674
def set_facecolors(self, c):
673675
"""alias for set_facecolor"""
@@ -685,38 +687,45 @@ def get_edgecolor(self):
685687
return self._edgecolors
686688
get_edgecolors = get_edgecolor
687689

688-
def set_edgecolor(self, c):
689-
"""
690-
Set the edgecolor(s) of the collection. *c* can be a
691-
matplotlib color spec (all patches have same color), or a
692-
sequence of specs; if it is a sequence the patches will
693-
cycle through the sequence.
694-
695-
If *c* is 'face', the edge color will always be the same as
696-
the face color. If it is 'none', the patch boundary will not
697-
be drawn.
698-
699-
ACCEPTS: matplotlib color spec or sequence of specs
700-
"""
690+
def _set_edgecolor(self, c):
691+
if c is None:
692+
if (mpl.rcParams['patch.force_edgecolor'] or
693+
not self._is_filled or self._edge_default):
694+
c = mpl.rcParams['patch.edgecolor']
695+
else:
696+
c = 'none'
701697
self._is_stroked = True
702698
try:
703699
if c.lower() == 'none':
704700
self._is_stroked = False
705701
except AttributeError:
706702
pass
703+
707704
try:
708-
if c.lower() == 'face':
705+
if c.lower() == 'face': # Special case: lookup in "get" method.
709706
self._edgecolors = 'face'
710-
self._edgecolors_original = 'face'
711707
return
712708
except AttributeError:
713709
pass
714-
if c is None:
715-
c = mpl.rcParams['patch.edgecolor']
716-
self._edgecolors_original = c
717710
self._edgecolors = mcolors.to_rgba_array(c, self._alpha)
718711
self.stale = True
719712

713+
def set_edgecolor(self, c):
714+
"""
715+
Set the edgecolor(s) of the collection. *c* can be a
716+
matplotlib color spec (all patches have same color), or a
717+
sequence of specs; if it is a sequence the patches will
718+
cycle through the sequence.
719+
720+
If *c* is 'face', the edge color will always be the same as
721+
the face color. If it is 'none', the patch boundary will not
722+
be drawn.
723+
724+
ACCEPTS: matplotlib color spec or sequence of specs
725+
"""
726+
self._original_edgecolor = c
727+
self._set_edgecolor(c)
728+
720729
def set_edgecolors(self, c):
721730
"""alias for set_edgecolor"""
722731
return self.set_edgecolor(c)
@@ -734,18 +743,8 @@ def set_alpha(self, alpha):
734743
except TypeError:
735744
raise TypeError('alpha must be a float or None')
736745
artist.Artist.set_alpha(self, alpha)
737-
try:
738-
self._facecolors = mcolors.to_rgba_array(
739-
self._facecolors_original, self._alpha)
740-
except (AttributeError, TypeError, IndexError):
741-
pass
742-
try:
743-
if (not isinstance(self._edgecolors_original, six.string_types)
744-
or self._edgecolors_original != str('face')):
745-
self._edgecolors = mcolors.to_rgba_array(
746-
self._edgecolors_original, self._alpha)
747-
except (AttributeError, TypeError, IndexError):
748-
pass
746+
self._set_facecolor(self._original_facecolor)
747+
self._set_edgecolor(self._original_edgecolor)
749748

750749
def get_linewidths(self):
751750
return self._linewidths
@@ -781,9 +780,9 @@ def update_from(self, other):
781780

782781
artist.Artist.update_from(self, other)
783782
self._antialiaseds = other._antialiaseds
784-
self._edgecolors_original = other._edgecolors_original
783+
self._original_edgecolor = other._original_edgecolor
785784
self._edgecolors = other._edgecolors
786-
self._facecolors_original = other._facecolors_original
785+
self._original_facecolor = other._original_facecolor
787786
self._facecolors = other._facecolors
788787
self._linewidths = other._linewidths
789788
self._linestyles = other._linestyles

lib/matplotlib/mpl-data/stylelib/classic.mplstyle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ markers.fillstyle: full
2929
# information on patch properties
3030
patch.linewidth : 1.0 # edge width in points
3131
patch.facecolor : b
32+
patch.force_edgecolor : True
3233
patch.edgecolor : k
3334
patch.antialiased : True # render patches in antialiased (no jaggies)
3435

lib/matplotlib/patches.py

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -154,18 +154,26 @@ def get_verts(self):
154154
return polygons[0]
155155
return []
156156

157+
def _process_radius(self, radius):
158+
if radius is not None:
159+
return radius
160+
if cbook.is_numlike(self._picker):
161+
_radius = self._picker
162+
else:
163+
if self.get_edgecolor()[3] == 0:
164+
_radius = 0
165+
else:
166+
_radius = self.get_linewidth()
167+
return _radius
168+
157169
def contains(self, mouseevent, radius=None):
158170
"""Test whether the mouse event occurred in the patch.
159171
160172
Returns T/F, {}
161173
"""
162174
if six.callable(self._contains):
163175
return self._contains(self, mouseevent)
164-
if radius is None:
165-
if cbook.is_numlike(self._picker):
166-
radius = self._picker
167-
else:
168-
radius = self.get_linewidth()
176+
radius = self._process_radius(radius)
169177
inside = self.get_path().contains_point(
170178
(mouseevent.x, mouseevent.y), self.get_transform(), radius)
171179
return inside, {}
@@ -175,11 +183,7 @@ def contains_point(self, point, radius=None):
175183
Returns *True* if the given point is inside the path
176184
(transformed with its transform attribute).
177185
"""
178-
if radius is None:
179-
if cbook.is_numlike(self._picker):
180-
radius = self._picker
181-
else:
182-
radius = self.get_linewidth()
186+
radius = self._process_radius(radius)
183187
return self.get_path().contains_point(point,
184188
self.get_transform(),
185189
radius)
@@ -281,37 +285,44 @@ def set_aa(self, aa):
281285
"""alias for set_antialiased"""
282286
return self.set_antialiased(aa)
283287

288+
def _set_edgecolor(self, color):
289+
if color is None:
290+
if (mpl.rcParams['patch.force_edgecolor'] or
291+
not self._fill or self._edge_default):
292+
color = mpl.rcParams['patch.edgecolor']
293+
else:
294+
color = 'none'
295+
self._edgecolor = colors.to_rgba(color, self._alpha)
296+
self.stale = True
297+
284298
def set_edgecolor(self, color):
285299
"""
286300
Set the patch edge color
287301
288-
ACCEPTS: mpl color spec, or None for default, or 'none' for no color
302+
ACCEPTS: mpl color spec, None, 'none', or 'auto'
289303
"""
290-
if color is None:
291-
color = mpl.rcParams['patch.edgecolor']
292304
self._original_edgecolor = color
293-
self._edgecolor = colors.to_rgba(color, self._alpha)
294-
self.stale = True
305+
self._set_edgecolor(color)
295306

296307
def set_ec(self, color):
297308
"""alias for set_edgecolor"""
298309
return self.set_edgecolor(color)
299310

311+
def _set_facecolor(self, color):
312+
if color is None:
313+
color = mpl.rcParams['patch.facecolor']
314+
alpha = self._alpha if self._fill else 0
315+
self._facecolor = colors.to_rgba(color, alpha)
316+
self.stale = True
317+
300318
def set_facecolor(self, color):
301319
"""
302320
Set the patch face color
303321
304322
ACCEPTS: mpl color spec, or None for default, or 'none' for no color
305323
"""
306-
if color is None:
307-
color = mpl.rcParams['patch.facecolor']
308-
# save: otherwise changing _fill may lose alpha information
309324
self._original_facecolor = color
310-
self._facecolor = colors.to_rgba(color, self._alpha)
311-
if not self._fill:
312-
self._facecolor = list(self._facecolor)
313-
self._facecolor[3] = 0
314-
self.stale = True
325+
self._set_facecolor(color)
315326

316327
def set_fc(self, color):
317328
"""alias for set_facecolor"""
@@ -343,10 +354,9 @@ def set_alpha(self, alpha):
343354
except TypeError:
344355
raise TypeError('alpha must be a float or None')
345356
artist.Artist.set_alpha(self, alpha)
346-
# using self._fill and self._alpha
347-
self.set_facecolor(self._original_facecolor)
348-
self.set_edgecolor(self._original_edgecolor)
349-
self.stale = True
357+
self._set_facecolor(self._facecolor)
358+
self._set_edgecolor(self._original_edgecolor)
359+
# stale is already True
350360

351361
def set_linewidth(self, w):
352362
"""
@@ -355,14 +365,9 @@ def set_linewidth(self, w):
355365
ACCEPTS: float or None for default
356366
"""
357367
if w is None:
358-
if (not self._fill or
359-
self._edge_default or
360-
mpl.rcParams['_internal.classic_mode']):
361-
w = mpl.rcParams['patch.linewidth']
362-
if w is None:
363-
w = mpl.rcParams['axes.linewidth']
364-
else:
365-
w = 0
368+
w = mpl.rcParams['patch.linewidth']
369+
if w is None:
370+
w = mpl.rcParams['axes.linewidth']
366371

367372
self._linewidth = float(w)
368373
# scale the dash pattern by the linewidth
@@ -428,7 +433,8 @@ def set_fill(self, b):
428433
ACCEPTS: [True | False]
429434
"""
430435
self._fill = bool(b)
431-
self.set_facecolor(self._original_facecolor)
436+
self._set_facecolor(self._original_facecolor)
437+
self._set_edgecolor(self._original_edgecolor)
432438
self.stale = True
433439

434440
def get_fill(self):

lib/matplotlib/rcsetup.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ def validate_color(s):
364364
'return a valid color arg'
365365
try:
366366
if s.lower() == 'none':
367-
return 'None'
367+
return 'none'
368368
except AttributeError:
369369
pass
370370

@@ -891,7 +891,7 @@ def validate_hist_bins(s):
891891
'lines.linewidth': [1.5, validate_float], # line width in points
892892
'lines.linestyle': ['-', six.text_type], # solid line
893893
'lines.color': ['C0', validate_color], # first color in color cycle
894-
'lines.marker': ['None', six.text_type], # black
894+
'lines.marker': ['None', six.text_type], # marker name
895895
'lines.markeredgewidth': [1.0, validate_float],
896896
'lines.markersize': [6, validate_float], # markersize, in points
897897
'lines.antialiased': [True, validate_bool], # antialiased (no jaggies)
@@ -907,10 +907,11 @@ def validate_hist_bins(s):
907907
'markers.fillstyle': ['full', validate_fillstyle],
908908

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

915916
## hatch props
916917
'hatch.linewidth': [1.0, validate_float],

lib/matplotlib/tests/test_artist.py

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -181,21 +181,20 @@ def test_remove():
181181
@image_comparison(baseline_images=["default_edges"], remove_text=True,
182182
extensions=['png'], style='default')
183183
def test_default_edges():
184-
with mpl.rc_context({'patch.linewidth': None}):
185-
fig, [[ax1, ax2], [ax3, ax4]] = plt.subplots(2, 2)
186-
187-
ax1.plot(np.arange(10), np.arange(10), 'x',
188-
np.arange(10) + 1, np.arange(10), 'o')
189-
ax2.bar(np.arange(10), np.arange(10))
190-
ax3.text(0, 0, "BOX", size=24, bbox=dict(boxstyle='sawtooth'))
191-
ax3.set_xlim((-1, 1))
192-
ax3.set_ylim((-1, 1))
193-
pp1 = mpatches.PathPatch(
194-
mpath.Path([(0, 0), (1, 0), (1, 1), (0, 0)],
195-
[mpath.Path.MOVETO, mpath.Path.CURVE3,
196-
mpath.Path.CURVE3, mpath.Path.CLOSEPOLY]),
197-
fc="none", transform=ax4.transData)
198-
ax4.add_patch(pp1)
184+
fig, [[ax1, ax2], [ax3, ax4]] = plt.subplots(2, 2)
185+
186+
ax1.plot(np.arange(10), np.arange(10), 'x',
187+
np.arange(10) + 1, np.arange(10), 'o')
188+
ax2.bar(np.arange(10), np.arange(10))
189+
ax3.text(0, 0, "BOX", size=24, bbox=dict(boxstyle='sawtooth'))
190+
ax3.set_xlim((-1, 1))
191+
ax3.set_ylim((-1, 1))
192+
pp1 = mpatches.PathPatch(
193+
mpath.Path([(0, 0), (1, 0), (1, 1), (0, 0)],
194+
[mpath.Path.MOVETO, mpath.Path.CURVE3,
195+
mpath.Path.CURVE3, mpath.Path.CLOSEPOLY]),
196+
fc="none", transform=ax4.transData)
197+
ax4.add_patch(pp1)
199198

200199

201200
@cleanup

0 commit comments

Comments
 (0)