3

I asked earlier on the matplotlib-user mailing list so apologies for the cross-post.

Say I have a marker with a known size in points and I want to draw an arrow to this point. How can I get the ends points for the arrow? As you can see in the below, it overlaps the markers. I want to go to the edge. I can use shrinkA and shrinkB to do what I want, but I don't see how they're related to the points size**.5. Or should I somehow do the transformation using the known angle between the two points and the point itself. I don't know how to translate a point in data coordinates and the offset it in a certain direction by size**.5 points. Can anyone help clear this up?

import matplotlib.pyplot as plt
from matplotlib.patches import FancyArrowPatch

point1 = (138.21, 19.5)
x1, y1 = point1
point2 = (67.0, 30.19)
x2, y2 = point2
size = 700

fig, ax = plt.subplots()
ax.scatter(*zip(point1, point2), marker='o', s=size)

# if I need to get and use the angles
dx = x2 - x1
dy = y2 - y1
d = np.sqrt(dx**2 + dy**2)

arrows = FancyArrowPatch(posA=(x1, y1), posB=(x2, y2),
                            color = 'k',
                            arrowstyle="-|>",
                            mutation_scale=700**.5,
                            connectionstyle="arc3")

ax.add_patch(arrows)

Edit: I made a little more progress. If I read the Translations Tutorial correctly, then this should give me a point on the radius of the markers. However, as soon as you resize the Axes then the transformation will be off. I'm stumped on what else to use.

from matplotlib.transforms import ScaledTranslation
# shift size points over and size points down so you should be on radius
# a point is 1/72 inches
dpi = ax.figure.get_dpi()
node_size = size**.5 / 2. # this is the radius of the marker
offset = ScaledTranslation(node_size/dpi, -node_size/dpi, fig.dpi_scale_trans)
shadow_transform = ax.transData + offset
ax.plot([x2], [y2], 'o', transform=shadow_transform, color='r')

Example

jseabold
  • 7,903
  • 2
  • 39
  • 53
  • I suspect you need to add a callback on `resize_event` http://matplotlib.org/users/event_handling.html to update your transform (via `set_transform` http://matplotlib.org/api/artist_api.html?highlight=line2d#matplotlib.artist.Artist.set_transform) – tacaswell Jan 31 '13 at 04:21
  • Did you get this sorted out? – tacaswell Oct 05 '13 at 01:00
  • @tacaswell I know this is old but [here](https://stackoverflow.com/questions/47383320/arrow-pointing-to-edge-of-marker-independently-from-markers-size) is a possible solution to the problem when markers are circles. – rodgdor Nov 20 '17 at 07:33

1 Answers1

0
import matplotlib.pyplot as plt
from matplotlib.patches import FancyArrowPatch
from matplotlib.transforms import ScaledTranslation

point1 = (138.21, 19.5)
x1, y1 = point1
point2 = (67.0, 30.19)
x2, y2 = point2
size = 700

fig, ax = plt.subplots()
ax.scatter(*zip(point1, point2), marker='o', s=size)

# if I need to get and use the angles
dx = x2 - x1
dy = y2 - y1
d = np.sqrt(dx**2 + dy**2)

arrows = FancyArrowPatch(posA=(x1, y1), posB=(x2, y2),
                            color = 'k',
                            arrowstyle="-|>",
                            mutation_scale=700**.5,
                            connectionstyle="arc3")

ax.add_patch(arrows)


# shift size points over and size points down so you should be on radius
# a point is 1/72 inches
def trans_callback(event):
    dpi = fig.get_dpi()
    node_size = size**.5 / 2. # this is the radius of the marker
    offset = ScaledTranslation(node_size/dpi, -node_size/dpi, fig.dpi_scale_trans)
    shadow_transform = ax.transData + offset
    arrows.set_transform(shadow_transform)


cid = fig.canvas.mpl_connect('resize_event', trans_callback)

You also need to include something about the aspect ratio of the axes get points on the rim of the point (because the shape of the marker in data units in an ellipse unless the aspect ratio = 1)

tacaswell
  • 84,579
  • 22
  • 210
  • 199