Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4d2aae6

Browse filesBrowse files
authored
Merge pull request #16959 from anntzer/connpatch
Simplify and robustify ConnectionPatch coordinates conversion.
2 parents f843ffd + 952a322 commit 4d2aae6
Copy full SHA for 4d2aae6

File tree

Expand file treeCollapse file tree

2 files changed

+61
-115
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+61
-115
lines changed

‎lib/matplotlib/patches.py

Copy file name to clipboardExpand all lines: lib/matplotlib/patches.py
+40-105Lines changed: 40 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -4243,108 +4243,54 @@ def __init__(self, xyA, xyB, coordsA, coordsB=None,
42434243
# if True, draw annotation only if self.xy is inside the axes
42444244
self._annotation_clip = None
42454245

4246-
def _get_xy(self, x, y, s, axes=None):
4246+
def _get_xy(self, xy, s, axes=None):
42474247
"""Calculate the pixel position of given point."""
4248+
s0 = s # For the error message, if needed.
42484249
if axes is None:
42494250
axes = self.axes
4251+
xy = np.array(xy)
4252+
if s in ["figure points", "axes points"]:
4253+
xy *= self.figure.dpi / 72
4254+
s = s.replace("points", "pixels")
4255+
elif s == "figure fraction":
4256+
s = self.figure.transFigure
4257+
elif s == "axes fraction":
4258+
s = axes.transAxes
4259+
x, y = xy
42504260

42514261
if s == 'data':
42524262
trans = axes.transData
42534263
x = float(self.convert_xunits(x))
42544264
y = float(self.convert_yunits(y))
42554265
return trans.transform((x, y))
42564266
elif s == 'offset points':
4257-
# convert the data point
4258-
dx, dy = self.xy
4259-
4260-
# prevent recursion
4261-
if self.xycoords == 'offset points':
4262-
return self._get_xy(dx, dy, 'data')
4263-
4264-
dx, dy = self._get_xy(dx, dy, self.xycoords)
4265-
4266-
# convert the offset
4267-
dpi = self.figure.get_dpi()
4268-
x *= dpi / 72.
4269-
y *= dpi / 72.
4270-
4271-
# add the offset to the data point
4272-
x += dx
4273-
y += dy
4274-
4275-
return x, y
4267+
if self.xycoords == 'offset points': # prevent recursion
4268+
return self._get_xy(self.xy, 'data')
4269+
return (
4270+
self._get_xy(self.xy, self.xycoords) # converted data point
4271+
+ xy * self.figure.dpi / 72) # converted offset
42764272
elif s == 'polar':
42774273
theta, r = x, y
42784274
x = r * np.cos(theta)
42794275
y = r * np.sin(theta)
42804276
trans = axes.transData
42814277
return trans.transform((x, y))
4282-
elif s == 'figure points':
4283-
# points from the lower left corner of the figure
4284-
dpi = self.figure.dpi
4285-
l, b, w, h = self.figure.bbox.bounds
4286-
r = l + w
4287-
t = b + h
4288-
4289-
x *= dpi / 72.
4290-
y *= dpi / 72.
4291-
if x < 0:
4292-
x = r + x
4293-
if y < 0:
4294-
y = t + y
4295-
return x, y
42964278
elif s == 'figure pixels':
42974279
# pixels from the lower left corner of the figure
4298-
l, b, w, h = self.figure.bbox.bounds
4299-
r = l + w
4300-
t = b + h
4301-
if x < 0:
4302-
x = r + x
4303-
if y < 0:
4304-
y = t + y
4305-
return x, y
4306-
elif s == 'figure fraction':
4307-
# (0, 0) is lower left, (1, 1) is upper right of figure
4308-
trans = self.figure.transFigure
4309-
return trans.transform((x, y))
4310-
elif s == 'axes points':
4311-
# points from the lower left corner of the axes
4312-
dpi = self.figure.dpi
4313-
l, b, w, h = axes.bbox.bounds
4314-
r = l + w
4315-
t = b + h
4316-
if x < 0:
4317-
x = r + x * dpi / 72.
4318-
else:
4319-
x = l + x * dpi / 72.
4320-
if y < 0:
4321-
y = t + y * dpi / 72.
4322-
else:
4323-
y = b + y * dpi / 72.
4280+
bb = self.figure.bbox
4281+
x = bb.x0 + x if x >= 0 else bb.x1 + x
4282+
y = bb.y0 + y if y >= 0 else bb.y1 + y
43244283
return x, y
43254284
elif s == 'axes pixels':
43264285
# pixels from the lower left corner of the axes
4327-
l, b, w, h = axes.bbox.bounds
4328-
r = l + w
4329-
t = b + h
4330-
if x < 0:
4331-
x = r + x
4332-
else:
4333-
x = l + x
4334-
if y < 0:
4335-
y = t + y
4336-
else:
4337-
y = b + y
4286+
bb = axes.bbox
4287+
x = bb.x0 + x if x >= 0 else bb.x1 + x
4288+
y = bb.y0 + y if y >= 0 else bb.y1 + y
43384289
return x, y
4339-
elif s == 'axes fraction':
4340-
# (0, 0) is lower left, (1, 1) is upper right of axes
4341-
trans = axes.transAxes
4342-
return trans.transform((x, y))
43434290
elif isinstance(s, transforms.Transform):
4344-
return s.transform((x, y))
4291+
return s.transform(xy)
43454292
else:
4346-
raise ValueError("{} is not a valid coordinate "
4347-
"transformation.".format(s))
4293+
raise ValueError(f"{s0} is not a valid coordinate transformation")
43484294

43494295
def set_annotation_clip(self, b):
43504296
"""
@@ -4374,39 +4320,29 @@ def get_annotation_clip(self):
43744320

43754321
def get_path_in_displaycoord(self):
43764322
"""Return the mutated path of the arrow in display coordinates."""
4377-
43784323
dpi_cor = self.get_dpi_cor()
4379-
4380-
x, y = self.xy1
4381-
posA = self._get_xy(x, y, self.coords1, self.axesA)
4382-
4383-
x, y = self.xy2
4384-
posB = self._get_xy(x, y, self.coords2, self.axesB)
4385-
4386-
_path = self.get_connectionstyle()(posA, posB,
4387-
patchA=self.patchA,
4388-
patchB=self.patchB,
4389-
shrinkA=self.shrinkA * dpi_cor,
4390-
shrinkB=self.shrinkB * dpi_cor
4391-
)
4392-
4393-
_path, fillable = self.get_arrowstyle()(
4394-
_path,
4395-
self.get_mutation_scale() * dpi_cor,
4396-
self.get_linewidth() * dpi_cor,
4397-
self.get_mutation_aspect()
4398-
)
4399-
4400-
return _path, fillable
4324+
posA = self._get_xy(self.xy1, self.coords1, self.axesA)
4325+
posB = self._get_xy(self.xy2, self.coords2, self.axesB)
4326+
path = self.get_connectionstyle()(
4327+
posA, posB,
4328+
patchA=self.patchA, patchB=self.patchB,
4329+
shrinkA=self.shrinkA * dpi_cor, shrinkB=self.shrinkB * dpi_cor,
4330+
)
4331+
path, fillable = self.get_arrowstyle()(
4332+
path,
4333+
self.get_mutation_scale() * dpi_cor,
4334+
self.get_linewidth() * dpi_cor,
4335+
self.get_mutation_aspect()
4336+
)
4337+
return path, fillable
44014338

44024339
def _check_xy(self, renderer):
44034340
"""Check whether the annotation needs to be drawn."""
44044341

44054342
b = self.get_annotation_clip()
44064343

44074344
if b or (b is None and self.coords1 == "data"):
4408-
x, y = self.xy1
4409-
xy_pixel = self._get_xy(x, y, self.coords1, self.axesA)
4345+
xy_pixel = self._get_xy(self.xy1, self.coords1, self.axesA)
44104346
if self.axesA is None:
44114347
axes = self.axes
44124348
else:
@@ -4415,8 +4351,7 @@ def _check_xy(self, renderer):
44154351
return False
44164352

44174353
if b or (b is None and self.coords2 == "data"):
4418-
x, y = self.xy2
4419-
xy_pixel = self._get_xy(x, y, self.coords2, self.axesB)
4354+
xy_pixel = self._get_xy(self.xy2, self.coords2, self.axesB)
44204355
if self.axesB is None:
44214356
axes = self.axes
44224357
else:

‎lib/matplotlib/tests/test_patches.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_patches.py
+21-10Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -394,16 +394,27 @@ def test_connection_patch():
394394
ax2.add_artist(con)
395395

396396

397-
def test_connection_patch_fig():
398-
# Test that connection patch can be added as figure artist
399-
fig, (ax1, ax2) = plt.subplots(1, 2)
400-
xy = (0.3, 0.2)
401-
con = mpatches.ConnectionPatch(xyA=xy, xyB=xy,
402-
coordsA="data", coordsB="data",
403-
axesA=ax1, axesB=ax2,
404-
arrowstyle="->", shrinkB=5)
405-
fig.add_artist(con)
406-
fig.canvas.draw()
397+
@check_figures_equal(extensions=["png"])
398+
def test_connection_patch_fig(fig_test, fig_ref):
399+
# Test that connection patch can be added as figure artist, and that figure
400+
# pixels count negative values from the top right corner (this API may be
401+
# changed in the future).
402+
ax1, ax2 = fig_test.subplots(1, 2)
403+
con = mpatches.ConnectionPatch(
404+
xyA=(.3, .2), coordsA="data", axesA=ax1,
405+
xyB=(-30, -20), coordsB="figure pixels",
406+
arrowstyle="->", shrinkB=5)
407+
fig_test.add_artist(con)
408+
409+
ax1, ax2 = fig_ref.subplots(1, 2)
410+
bb = fig_ref.bbox
411+
# Necessary so that pixel counts match on both sides.
412+
plt.rcParams["savefig.dpi"] = plt.rcParams["figure.dpi"]
413+
con = mpatches.ConnectionPatch(
414+
xyA=(.3, .2), coordsA="data", axesA=ax1,
415+
xyB=(bb.width - 30, bb.height - 20), coordsB="figure pixels",
416+
arrowstyle="->", shrinkB=5)
417+
fig_ref.add_artist(con)
407418

408419

409420
def test_datetime_rectangle():

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.