Skip to content

Commit 054971d

Browse files
committed
Factor out common code between Patch.draw and FancyArrowPatch.draw.
The code setting up the renderer and gc are nearly identical, so we can factor it out as a helper contextmanager. The only difference is that FancyArrowPatch then calls draw_path multiple times with various arguments. Note that the refactoring exposed the fact that FancyArrowPatches are (incorrectly, IMO) ignoring joinstyles and capstyles (this is necessary to make image test pass, but we probably will want to generate new images with the "correct" (non-ignoring) behavior at some point).
1 parent 38476fd commit 054971d

File tree

1 file changed

+41
-68
lines changed

1 file changed

+41
-68
lines changed

lib/matplotlib/patches.py

+41-68
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import contextlib
12
import functools
23
import math
34
from numbers import Number
@@ -479,11 +480,15 @@ def get_hatch(self):
479480
'Return the current hatching pattern'
480481
return self._hatch
481482

482-
@artist.allow_rasterization
483-
def draw(self, renderer):
484-
'Draw the :class:`Patch` to the given *renderer*.'
485-
if not self.get_visible():
486-
return
483+
@contextlib.contextmanager
484+
def _prepare_path_drawer(self, renderer):
485+
"""
486+
``draw()`` helper factored out for sharing with `FancyArrowPatch`.
487+
488+
Yields a callable that can be called as a bound ``draw_path`` method
489+
except that the first argument (the ``GraphicsContext``) has already
490+
been suitably filled in.
491+
"""
487492

488493
renderer.open_group('patch', self.get_gid())
489494
gc = renderer.new_gc()
@@ -521,21 +526,33 @@ def draw(self, renderer):
521526
if self.get_sketch_params() is not None:
522527
gc.set_sketch_params(*self.get_sketch_params())
523528

524-
path = self.get_path()
525-
transform = self.get_transform()
526-
tpath = transform.transform_path_non_affine(path)
527-
affine = transform.get_affine()
528-
529529
if self.get_path_effects():
530530
from matplotlib.patheffects import PathEffectRenderer
531531
renderer = PathEffectRenderer(self.get_path_effects(), renderer)
532532

533-
renderer.draw_path(gc, tpath, affine, rgbFace)
533+
yield functools.partial(renderer.draw_path, gc)
534534

535535
gc.restore()
536536
renderer.close_group('patch')
537537
self.stale = False
538538

539+
@artist.allow_rasterization
540+
def draw(self, renderer):
541+
'Draw the :class:`Patch` to the given *renderer*.'
542+
if not self.get_visible():
543+
return
544+
545+
with self._prepare_path_drawer(renderer) as draw_path:
546+
path = self.get_path()
547+
transform = self.get_transform()
548+
tpath = transform.transform_path_non_affine(path)
549+
affine = transform.get_affine()
550+
draw_path(tpath, affine,
551+
# Work around a bug in the PDF and SVG renderers, which
552+
# do not draw the hatches if the facecolor is fully
553+
# transparent, but do if it is None.
554+
self._facecolor if self._facecolor[3] else None)
555+
539556
def get_path(self):
540557
"""
541558
Return the path of this patch
@@ -4256,67 +4273,23 @@ def draw(self, renderer):
42564273
if not self.get_visible():
42574274
return
42584275

4259-
renderer.open_group('patch', self.get_gid())
4260-
gc = renderer.new_gc()
4261-
4262-
gc.set_foreground(self._edgecolor, isRGBA=True)
4263-
4264-
lw = self._linewidth
4265-
if self._edgecolor[3] == 0:
4266-
lw = 0
4267-
gc.set_linewidth(lw)
4268-
gc.set_dashes(self._dashoffset, self._dashes)
4269-
4270-
gc.set_antialiased(self._antialiased)
4271-
self._set_gc_clip(gc)
4272-
gc.set_capstyle('round')
4273-
gc.set_snap(self.get_snap())
4274-
4275-
rgbFace = self._facecolor
4276-
if rgbFace[3] == 0:
4277-
rgbFace = None # (some?) renderers expect this as no-fill signal
4278-
4279-
gc.set_alpha(self._alpha)
4280-
4281-
if self._hatch:
4282-
gc.set_hatch(self._hatch)
4283-
if self._hatch_color is not None:
4284-
try:
4285-
gc.set_hatch_color(self._hatch_color)
4286-
except AttributeError:
4287-
# if we end up with a GC that does not have this method
4288-
warnings.warn("Your backend does not support setting the "
4289-
"hatch color.")
4290-
4291-
if self.get_sketch_params() is not None:
4292-
gc.set_sketch_params(*self.get_sketch_params())
4276+
# FancyArrowPatch have traditionally forced the capstyle and joinstyle.
4277+
with cbook._setattr_cm(self, _capstyle='round', _joinstyle='round'), \
4278+
self._prepare_path_drawer(renderer) as draw_path:
42934279

4294-
# FIXME : dpi_cor is for the dpi-dependecy of the
4295-
# linewidth. There could be room for improvement.
4296-
#
4297-
# dpi_cor = renderer.points_to_pixels(1.)
4298-
self.set_dpi_cor(renderer.points_to_pixels(1.))
4299-
path, fillable = self.get_path_in_displaycoord()
4280+
# FIXME : dpi_cor is for the dpi-dependecy of the linewidth. There
4281+
# could be room for improvement.
4282+
self.set_dpi_cor(renderer.points_to_pixels(1.))
4283+
path, fillable = self.get_path_in_displaycoord()
43004284

4301-
if not cbook.iterable(fillable):
4302-
path = [path]
4303-
fillable = [fillable]
4285+
if not cbook.iterable(fillable):
4286+
path = [path]
4287+
fillable = [fillable]
43044288

4305-
affine = transforms.IdentityTransform()
4289+
affine = transforms.IdentityTransform()
43064290

4307-
if self.get_path_effects():
4308-
from matplotlib.patheffects import PathEffectRenderer
4309-
renderer = PathEffectRenderer(self.get_path_effects(), renderer)
4310-
4311-
for p, f in zip(path, fillable):
4312-
if f:
4313-
renderer.draw_path(gc, p, affine, rgbFace)
4314-
else:
4315-
renderer.draw_path(gc, p, affine, None)
4316-
4317-
gc.restore()
4318-
renderer.close_group('patch')
4319-
self.stale = False
4291+
for p, f in zip(path, fillable):
4292+
draw_path(p, affine, self._facecolor if f else None)
43204293

43214294

43224295
class ConnectionPatch(FancyArrowPatch):

0 commit comments

Comments
 (0)