Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f2aa4b2

Browse files
committed
Removed YAArrow usage from Annotation.
YAArrow is now partly simulated with the FancyArrowPatch, which remains as the only arrow class used by Annotation. I ignored the 'frac' key and added the 'headlength' key; the 'frac' was never a good API because it scaled head length with the arrow length, but left all other dimensions in units of points.
1 parent 25d259c commit f2aa4b2

File tree

2 files changed

+104
-112
lines changed

2 files changed

+104
-112
lines changed

lib/matplotlib/patches.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3119,14 +3119,14 @@ class ArrowStyle(_Style):
31193119
%(AvailableArrowstyles)s
31203120
31213121
3122-
An instance of any arrow style class is an callable object,
3122+
An instance of any arrow style class is a callable object,
31233123
whose call signature is::
31243124
31253125
__call__(self, path, mutation_size, linewidth, aspect_ratio=1.)
31263126
31273127
and it returns a tuple of a :class:`Path` instance and a boolean
3128-
value. *path* is a :class:`Path` instance along witch the arrow
3129-
will be drawn. *mutation_size* and *aspect_ratio* has a same
3128+
value. *path* is a :class:`Path` instance along which the arrow
3129+
will be drawn. *mutation_size* and *aspect_ratio* have the same
31303130
meaning as in :class:`BoxStyle`. *linewidth* is a line width to be
31313131
stroked. This is meant to be used to correct the location of the
31323132
head so that it does not overshoot the destination point, but not all
@@ -3175,11 +3175,11 @@ def ensure_quadratic_bezier(path):
31753175

31763176
def transmute(self, path, mutation_size, linewidth):
31773177
"""
3178-
The transmute method is a very core of the ArrowStyle
3178+
The transmute method is the very core of the ArrowStyle
31793179
class and must be overriden in the subclasses. It receives
31803180
the path object along which the arrow will be drawn, and
3181-
the mutation_size, with which the amount arrow head and
3182-
etc. will be scaled. The linewidth may be used to adjust
3181+
the mutation_size, with which the arrow head etc.
3182+
will be scaled. The linewidth may be used to adjust
31833183
the path so that it does not pass beyond the given
31843184
points. It returns a tuple of a Path instance and a
31853185
boolean. The boolean value indicate whether the path can
@@ -4077,7 +4077,7 @@ def set_connectionstyle(self, connectionstyle, **kw):
40774077
40784078
*connectionstyle* can be a string with connectionstyle name with
40794079
optional comma-separated attributes. Alternatively, the attrs can be
4080-
probided as keywords.
4080+
provided as keywords.
40814081
40824082
set_connectionstyle("arc,angleA=0,armA=30,rad=10")
40834083
set_connectionstyle("arc", angleA=0,armA=30,rad=10)

lib/matplotlib/text.py

Lines changed: 97 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from matplotlib.transforms import Affine2D, Bbox, Transform
2828
from matplotlib.transforms import BboxBase, BboxTransformTo
2929
from matplotlib.lines import Line2D
30-
30+
from matplotlib.path import Path
3131
from matplotlib.artist import allow_rasterization
3232

3333
from matplotlib.backend_bases import RendererBase
@@ -539,7 +539,7 @@ def update_bbox_position_size(self, renderer):
539539
self._bbox_patch.set_transform(tr)
540540
fontsize_in_pixel = renderer.points_to_pixels(self.get_size())
541541
self._bbox_patch.set_mutation_scale(fontsize_in_pixel)
542-
# self._bbox_patch.draw(renderer)
542+
543543

544544
def _draw_bbox(self, renderer, posx, posy):
545545

@@ -558,6 +558,7 @@ def _draw_bbox(self, renderer, posx, posy):
558558
self._bbox_patch.set_mutation_scale(fontsize_in_pixel)
559559
self._bbox_patch.draw(renderer)
560560

561+
561562
def _update_clip_properties(self):
562563
clipprops = dict(clip_box=self.clipbox,
563564
clip_path=self._clippath,
@@ -2046,9 +2047,18 @@ def __init__(self, s, xy,
20462047

20472048
self.arrow = None
20482049

2049-
if arrowprops and "arrowstyle" in arrowprops:
2050-
arrowprops = self.arrowprops.copy()
2051-
self._arrow_relpos = arrowprops.pop("relpos", (0.5, 0.5))
2050+
if arrowprops:
2051+
if "arrowstyle" in arrowprops:
2052+
arrowprops = self.arrowprops.copy()
2053+
self._arrow_relpos = arrowprops.pop("relpos", (0.5, 0.5))
2054+
else:
2055+
# modified YAArrow API to be used with FancyArrowPatch
2056+
shapekeys = ('width', 'headwidth', 'headlength',
2057+
'shrink', 'frac')
2058+
arrowprops = dict()
2059+
for key, val in self.arrowprops.items():
2060+
if key not in shapekeys:
2061+
arrowprops[key] = val # basic Patch properties
20522062
self.arrow_patch = FancyArrowPatch((0, 0), (1, 1),
20532063
**arrowprops)
20542064
else:
@@ -2059,7 +2069,9 @@ def contains(self, event):
20592069
if self.arrow is not None:
20602070
in_arrow, _ = self.arrow.contains(event)
20612071
contains = contains or in_arrow
2062-
# self.arrow_patch is currently not checked as this can be a line - J
2072+
if self.arrow_patch is not None:
2073+
in_patch, _ = self.arrow_patch.contains(event)
2074+
contains = contains or in_patch
20632075

20642076
return contains, tinfo
20652077

@@ -2102,6 +2114,7 @@ def _update_position_xytext(self, renderer, xy_pixel):
21022114
self.set_transform(self._get_xy_transform(
21032115
renderer, self.xy, self.anncoords))
21042116

2117+
21052118
ox0, oy0 = self._get_xy_display()
21062119
ox1, oy1 = xy_pixel
21072120

@@ -2114,111 +2127,94 @@ def _update_position_xytext(self, renderer, xy_pixel):
21142127
yc = 0.5 * (b + t)
21152128

21162129
d = self.arrowprops.copy()
2130+
ms = d.pop("mutation_scale", self.get_size())
2131+
ms = renderer.points_to_pixels(ms)
2132+
self.arrow_patch.set_mutation_scale(ms)
21172133

2118-
# Use FancyArrowPatch if self.arrowprops has "arrowstyle" key.
2119-
# Otherwise, fallback to YAArrow.
2120-
2121-
#if d.has_key("arrowstyle"):
2122-
if self.arrow_patch:
2123-
2124-
# adjust the starting point of the arrow relative to
2125-
# the textbox.
2126-
# TODO : Rotation needs to be accounted.
2127-
relpos = self._arrow_relpos
2128-
bbox = Text.get_window_extent(self, renderer)
2129-
ox0 = bbox.x0 + bbox.width * relpos[0]
2130-
oy0 = bbox.y0 + bbox.height * relpos[1]
2131-
2132-
# The arrow will be drawn from (ox0, oy0) to (ox1,
2133-
# oy1). It will be first clipped by patchA and patchB.
2134-
# Then it will be shrinked by shirnkA and shrinkB
2135-
# (in points). If patch A is not set, self.bbox_patch
2136-
# is used.
2137-
2138-
self.arrow_patch.set_positions((ox0, oy0), (ox1, oy1))
2139-
mutation_scale = d.pop("mutation_scale", self.get_size())
2140-
mutation_scale = renderer.points_to_pixels(mutation_scale)
2141-
self.arrow_patch.set_mutation_scale(mutation_scale)
2142-
2143-
if "patchA" in d:
2144-
self.arrow_patch.set_patchA(d.pop("patchA"))
2145-
else:
2146-
if self._bbox_patch:
2147-
self.arrow_patch.set_patchA(self._bbox_patch)
2148-
else:
2149-
pad = renderer.points_to_pixels(4)
2150-
if self.get_text().strip() == "":
2151-
self.arrow_patch.set_patchA(None)
2152-
return
2153-
2154-
bbox = Text.get_window_extent(self, renderer)
2155-
l, b, w, h = bbox.bounds
2156-
l -= pad / 2.
2157-
b -= pad / 2.
2158-
w += pad
2159-
h += pad
2160-
r = Rectangle(xy=(l, b),
2161-
width=w,
2162-
height=h,
2163-
)
2164-
r.set_transform(mtransforms.IdentityTransform())
2165-
r.set_clip_on(False)
2166-
2167-
self.arrow_patch.set_patchA(r)
2134+
if "arrowstyle" not in d:
2135+
# Approximately simulate the YAArrow.
2136+
# Pop its kwargs:
2137+
shrink = d.pop('shrink', 0.0)
2138+
width = d.pop('width', 4)
2139+
headwidth = d.pop('headwidth', 12)
2140+
# Ignore frac--it is useless.
2141+
frac = d.pop('frac', None)
2142+
if frac is not None:
2143+
warnings.warn(
2144+
"'frac' option in 'arrowstyle' is no longer supported;"
2145+
" use 'headlength' to set the head length in points.")
2146+
headlength = d.pop('headlength', 12)
21682147

2169-
else:
2170-
# using YAArrow
2148+
stylekw = dict(head_length=headlength / ms,
2149+
head_width=headwidth / ms,
2150+
tail_width=width / ms)
2151+
2152+
self.arrow_patch.set_arrowstyle('simple', **stylekw)
2153+
2154+
# using YAArrow style:
21712155
# pick the x,y corner of the text bbox closest to point
21722156
# annotated
2173-
dsu = [(abs(val - x0), val) for val in (l, r, xc)]
2157+
xpos = ((l, 0), (xc, 0.5), (r, 1))
2158+
ypos = ((b, 0), (yc, 0.5), (t, 1))
2159+
2160+
dsu = [(abs(val[0] - x0), val) for val in xpos]
21742161
dsu.sort()
2175-
_, x = dsu[0]
2162+
_, (x, relposx) = dsu[0]
21762163

2177-
dsu = [(abs(val - y0), val) for val in (b, t, yc)]
2164+
dsu = [(abs(val[0] - y0), val) for val in ypos]
21782165
dsu.sort()
2179-
_, y = dsu[0]
2166+
_, (y, relposy) = dsu[0]
21802167

2181-
shrink = d.pop('shrink', 0.0)
2168+
self._arrow_relpos = (relposx, relposy)
21822169

2183-
theta = math.atan2(y - y0, x - x0)
21842170
r = np.hypot((y - y0), (x - x0))
2185-
dx = shrink * r * math.cos(theta)
2186-
dy = shrink * r * math.sin(theta)
2171+
shrink_pts = shrink * r / renderer.points_to_pixels(1)
2172+
self.arrow_patch.shrinkA = shrink_pts
2173+
self.arrow_patch.shrinkB = shrink_pts
21872174

2188-
width = d.pop('width', 4)
2189-
headwidth = d.pop('headwidth', 12)
2190-
frac = d.pop('frac', 0.1)
2191-
self.arrow = YAArrow(self.figure,
2192-
(x0 + dx, y0 + dy), (x - dx, y - dy),
2193-
width=width, headwidth=headwidth,
2194-
frac=frac,
2195-
**d)
21962175

2197-
self.arrow.set_clip_box(self.get_clip_box())
2176+
# adjust the starting point of the arrow relative to
2177+
# the textbox.
2178+
# TODO : Rotation needs to be accounted.
2179+
relpos = self._arrow_relpos
2180+
bbox = Text.get_window_extent(self, renderer)
2181+
ox0 = bbox.x0 + bbox.width * relpos[0]
2182+
oy0 = bbox.y0 + bbox.height * relpos[1]
21982183

2199-
def update_bbox_position_size(self, renderer):
2200-
"""
2201-
Update the location and the size of the bbox. This method
2202-
should be used when the position and size of the bbox needs to
2203-
be updated before actually drawing the bbox.
2204-
"""
2184+
# The arrow will be drawn from (ox0, oy0) to (ox1,
2185+
# oy1). It will be first clipped by patchA and patchB.
2186+
# Then it will be shrunk by shirnkA and shrinkB
2187+
# (in points). If patch A is not set, self.bbox_patch
2188+
# is used.
22052189

2206-
# For arrow_patch, use textbox as patchA by default.
2190+
self.arrow_patch.set_positions((ox0, oy0), (ox1, oy1))
22072191

2208-
if not isinstance(self.arrow_patch, FancyArrowPatch):
2209-
return
2192+
if "patchA" in d:
2193+
self.arrow_patch.set_patchA(d.pop("patchA"))
2194+
else:
2195+
if self._bbox_patch:
2196+
self.arrow_patch.set_patchA(self._bbox_patch)
2197+
else:
2198+
pad = renderer.points_to_pixels(4)
2199+
if self.get_text().strip() == "":
2200+
self.arrow_patch.set_patchA(None)
2201+
return
2202+
2203+
bbox = Text.get_window_extent(self, renderer)
2204+
l, b, w, h = bbox.bounds
2205+
l -= pad / 2.
2206+
b -= pad / 2.
2207+
w += pad
2208+
h += pad
2209+
r = Rectangle(xy=(l, b),
2210+
width=w,
2211+
height=h,
2212+
)
2213+
r.set_transform(mtransforms.IdentityTransform())
2214+
r.set_clip_on(False)
2215+
2216+
self.arrow_patch.set_patchA(r)
22102217

2211-
if self._bbox_patch:
2212-
posx, posy = self._x, self._y
2213-
2214-
x_box, y_box, w_box, h_box = _get_textbox(self, renderer)
2215-
self._bbox_patch.set_bounds(0., 0., w_box, h_box)
2216-
theta = np.deg2rad(self.get_rotation())
2217-
tr = mtransforms.Affine2D().rotate(theta)
2218-
tr = tr.translate(posx + x_box, posy + y_box)
2219-
self._bbox_patch.set_transform(tr)
2220-
fontsize_in_pixel = renderer.points_to_pixels(self.get_size())
2221-
self._bbox_patch.set_mutation_scale(fontsize_in_pixel)
22222218

22232219
@allow_rasterization
22242220
def draw(self, renderer):
@@ -2238,20 +2234,16 @@ def draw(self, renderer):
22382234
self._update_position_xytext(renderer, xy_pixel)
22392235
self.update_bbox_position_size(renderer)
22402236

2241-
# Draw text, including FancyBboxPatch, before FancyArrowPatch.
2242-
# Otherwise, the transform of the former Patch will be incomplete.
2243-
Text.draw(self, renderer)
2244-
2245-
if self.arrow is not None:
2246-
if self.arrow.figure is None and self.figure is not None:
2247-
self.arrow.figure = self.figure
2248-
self.arrow.draw(renderer)
2249-
2250-
if self.arrow_patch is not None:
2237+
if self.arrow_patch is not None: # FancyArrowPatch
22512238
if self.arrow_patch.figure is None and self.figure is not None:
22522239
self.arrow_patch.figure = self.figure
22532240
self.arrow_patch.draw(renderer)
22542241

2242+
# Draw text, including FancyBboxPatch, after FancyArrowPatch.
2243+
# Otherwise, a wedge arrowstyle can land partly on top of the Bbox.
2244+
Text.draw(self, renderer)
2245+
2246+
22552247
def get_window_extent(self, renderer=None):
22562248
'''
22572249
Return a :class:`~matplotlib.transforms.Bbox` object bounding

0 commit comments

Comments
 (0)