Skip to content

Commit 36f1353

Browse files
authored
Merge pull request #6904 from efiring/scatter_edgecolor
MNT: Use edgecolor rather than linewidth to control edge display.
2 parents b2a25b2 + 69af0e7 commit 36f1353

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
@@ -128,9 +128,10 @@ def __init__(self,
128128
# list of unbroadcast/scaled linewidths
129129
self._us_lw = [0]
130130
self._linewidths = [0]
131+
self._is_filled = True # May be modified by set_facecolor().
131132

132-
self.set_edgecolor(edgecolors)
133133
self.set_facecolor(facecolors)
134+
self.set_edgecolor(edgecolors)
134135
self.set_linewidth(linewidths)
135136
self.set_linestyle(linestyles)
136137
self.set_antialiased(antialiaseds)
@@ -492,14 +493,9 @@ def set_linewidth(self, lw):
492493
ACCEPTS: float or sequence of floats
493494
"""
494495
if lw is None:
495-
if (self._edge_default or
496-
mpl.rcParams['_internal.classic_mode'] or
497-
not self._is_filled):
498-
lw = mpl.rcParams['patch.linewidth']
499-
if lw is None:
500-
lw = mpl.rcParams['lines.linewidth']
501-
else:
502-
lw = 0
496+
lw = mpl.rcParams['patch.linewidth']
497+
if lw is None:
498+
lw = mpl.rcParams['lines.linewidth']
503499
# get the un-scaled/broadcast lw
504500
self._us_lw = self._get_value(lw)
505501

@@ -644,6 +640,20 @@ def set_color(self, c):
644640
self.set_facecolor(c)
645641
self.set_edgecolor(c)
646642

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

670672
def set_facecolors(self, c):
671673
"""alias for set_facecolor"""
@@ -683,38 +685,45 @@ def get_edgecolor(self):
683685
return self._edgecolors
684686
get_edgecolors = get_edgecolor
685687

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

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

748747
def get_linewidths(self):
749748
return self._linewidths
@@ -779,9 +778,9 @@ def update_from(self, other):
779778

780779
artist.Artist.update_from(self, other)
781780
self._antialiaseds = other._antialiaseds
782-
self._edgecolors_original = other._edgecolors_original
781+
self._original_edgecolor = other._original_edgecolor
783782
self._edgecolors = other._edgecolors
784-
self._facecolors_original = other._facecolors_original
783+
self._original_facecolor = other._original_facecolor
785784
self._facecolors = other._facecolors
786785
self._linewidths = other._linewidths
787786
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

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

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

930931
## hatch props
931932
'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
@@ -182,21 +182,20 @@ def test_remove():
182182
@image_comparison(baseline_images=["default_edges"], remove_text=True,
183183
extensions=['png'], style='default')
184184
def test_default_edges():
185-
with mpl.rc_context({'patch.linewidth': None}):
186-
fig, [[ax1, ax2], [ax3, ax4]] = plt.subplots(2, 2)
187-
188-
ax1.plot(np.arange(10), np.arange(10), 'x',
189-
np.arange(10) + 1, np.arange(10), 'o')
190-
ax2.bar(np.arange(10), np.arange(10))
191-
ax3.text(0, 0, "BOX", size=24, bbox=dict(boxstyle='sawtooth'))
192-
ax3.set_xlim((-1, 1))
193-
ax3.set_ylim((-1, 1))
194-
pp1 = mpatches.PathPatch(
195-
mpath.Path([(0, 0), (1, 0), (1, 1), (0, 0)],
196-
[mpath.Path.MOVETO, mpath.Path.CURVE3,
197-
mpath.Path.CURVE3, mpath.Path.CLOSEPOLY]),
198-
fc="none", transform=ax4.transData)
199-
ax4.add_patch(pp1)
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)
200199

201200

202201
@cleanup

0 commit comments

Comments
 (0)