Skip to content

Commit 5c26a7e

Browse files
leejjoonmdboom
authored andcommitted
implement path_effects for Line2D objects
1 parent 50508af commit 5c26a7e

File tree

2 files changed

+95
-9
lines changed

2 files changed

+95
-9
lines changed

lib/matplotlib/lines.py

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ def __init__(self, xdata, ydata,
149149
pickradius=5,
150150
drawstyle=None,
151151
markevery=None,
152+
path_effects = None,
152153
**kwargs
153154
):
154155
"""
@@ -236,6 +237,8 @@ def __init__(self, xdata, ydata,
236237
self._invalidy = True
237238
self.set_data(xdata, ydata)
238239

240+
self.set_path_effects(path_effects)
241+
239242
def contains(self, mouseevent):
240243
"""
241244
Test whether the mouse event occurred on the line. The pick
@@ -550,7 +553,14 @@ def draw(self, renderer):
550553
self._lineFunc = getattr(self, funcname)
551554
funcname = self.drawStyles.get(self._drawstyle, '_draw_lines')
552555
drawFunc = getattr(self, funcname)
553-
drawFunc(renderer, gc, tpath, affine.frozen())
556+
557+
if self.get_path_effects():
558+
affine_frozen = affine.frozen()
559+
for pe in self.get_path_effects():
560+
pe_renderer = pe.get_proxy_renderer(renderer)
561+
drawFunc(pe_renderer, gc, tpath, affine_frozen)
562+
else:
563+
drawFunc(renderer, gc, tpath, affine.frozen())
554564

555565
if self._marker:
556566
gc = renderer.new_gc()
@@ -600,18 +610,34 @@ def draw(self, renderer):
600610
gc.set_linewidth(0)
601611
if rgbaFace is not None:
602612
gc.set_alpha(rgbaFace[3])
603-
renderer.draw_markers(
604-
gc, marker_path, marker_trans, subsampled, affine.frozen(),
605-
rgbaFace)
613+
614+
if self.get_path_effects():
615+
affine_frozen = affine.frozen()
616+
for pe in self.get_path_effects():
617+
pe.draw_markers(renderer, gc, marker_path, marker_trans,
618+
subsampled, affine_frozen, rgbaFace)
619+
else:
620+
renderer.draw_markers(
621+
gc, marker_path, marker_trans, subsampled, affine.frozen(),
622+
rgbaFace)
623+
606624
alt_marker_path = marker.get_alt_path()
607625
if alt_marker_path:
608626
if rgbaFaceAlt is not None:
609627
gc.set_alpha(rgbaFaceAlt[3])
610628
alt_marker_trans = marker.get_alt_transform()
611629
alt_marker_trans = alt_marker_trans.scale(w)
612-
renderer.draw_markers(
613-
gc, alt_marker_path, alt_marker_trans, subsampled,
614-
affine.frozen(), rgbaFaceAlt)
630+
631+
if self.get_path_effects():
632+
affine_frozen = affine.frozen()
633+
for pe in self.get_path_effects():
634+
pe.draw_markers(renderer, gc, alt_marker_path,
635+
alt_marker_trans, subsampled,
636+
affine_frozen, rgbaFaceAlt)
637+
else:
638+
renderer.draw_markers(
639+
gc, alt_marker_path, alt_marker_trans, subsampled,
640+
affine.frozen(), rgbaFaceAlt)
615641

616642
gc.restore()
617643

@@ -1161,6 +1187,16 @@ def is_dashed(self):
11611187
'return True if line is dashstyle'
11621188
return self._linestyle in ('--', '-.', ':')
11631189

1190+
def set_path_effects(self, path_effects):
1191+
"""
1192+
set path_effects, which should be a list of instances of
1193+
matplotlib.patheffect._Base class or its derivatives.
1194+
"""
1195+
self._path_effects = path_effects
1196+
1197+
def get_path_effects(self):
1198+
return self._path_effects
1199+
11641200

11651201
class VertexSelector:
11661202
"""

lib/matplotlib/patheffects.py

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import matplotlib.transforms as transforms
1111

1212

13-
1413
class _Base(object):
1514
"""
1615
A base class for PathEffect. Derived must override draw_path method.
@@ -22,6 +21,9 @@ def __init__(self):
2221
"""
2322
super(_Base, self).__init__()
2423

24+
def get_proxy_renderer(self, renderer):
25+
pe_renderer = ProxyRenderer(self, renderer)
26+
return pe_renderer
2527

2628
def _update_gc(self, gc, new_gc_dict):
2729
new_gc_dict = new_gc_dict.copy()
@@ -39,7 +41,7 @@ def _update_gc(self, gc, new_gc_dict):
3941
return gc
4042

4143

42-
def draw_path(self, renderer, gc, tpath, affine, rgbFace):
44+
def draw_path(self, renderer, gc, tpath, affine, rgbFace=None):
4345
"""
4446
Derived should override this method. The argument is same
4547
as *draw_path* method of :class:`matplotlib.backend_bases.RendererBase`
@@ -71,6 +73,33 @@ def _draw_text_as_path(self, renderer, gc, x, y, s, prop, angle, ismath):
7173
gc.set_linewidth(0.0)
7274
self.draw_path(renderer, gc, path, transform, rgbFace=color)
7375

76+
def draw_markers(self, renderer, gc, marker_path, marker_trans, path, trans, rgbFace=None):
77+
"""
78+
Draws a marker at each of the vertices in path. This includes
79+
all vertices, including control points on curves. To avoid
80+
that behavior, those vertices should be removed before calling
81+
this function.
82+
83+
*gc*
84+
the :class:`GraphicsContextBase` instance
85+
86+
*marker_trans*
87+
is an affine transform applied to the marker.
88+
89+
*trans*
90+
is an affine transform applied to the path.
91+
92+
This provides a fallback implementation of draw_markers that
93+
makes multiple calls to :meth:`draw_path`. Some backends may
94+
want to override this method in order to draw the marker only
95+
once and reuse it multiple times.
96+
"""
97+
for vertices, codes in path.iter_segments(trans, simplify=False):
98+
if len(vertices):
99+
x,y = vertices[-2:]
100+
self.draw_path(renderer, gc, marker_path,
101+
marker_trans + transforms.Affine2D().translate(x, y),
102+
rgbFace)
74103

75104
# def draw_path_collection(self, renderer,
76105
# gc, master_transform, paths, all_transforms,
@@ -88,6 +117,27 @@ def _draw_text_as_path(self, renderer, gc, x, y, s, prop, angle, ismath):
88117
# transform = transforms.Affine2D(transform.get_matrix()).translate(xo, yo)
89118
# self.draw_path(renderer, gc0, path, transform, rgbFace)
90119

120+
class ProxyRenderer(object):
121+
def __init__(self, path_effect, renderer):
122+
self._path_effect = path_effect
123+
self._renderer = renderer
124+
125+
def draw_path(self, gc, tpath, affine, rgbFace=None):
126+
self._path_effect.draw_path(self._renderer, gc, tpath, affine, rgbFace)
127+
128+
def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!'):
129+
self._path_effect._draw_text_as_path(self._renderer,
130+
gc, x, y, s, prop, angle, ismath="TeX")
131+
132+
def draw_text(self, gc, x, y, s, prop, angle, ismath=False):
133+
self._path_effect._draw_text(self.renderer,
134+
gc, x, y, s, prop, angle, ismath)
135+
136+
def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
137+
self._path_effect.draw_markers(self._renderer,
138+
gc, marker_path, marker_trans, path, trans,
139+
rgbFace=rgbFace)
140+
91141

92142
class Normal(_Base):
93143
"""

0 commit comments

Comments
 (0)