8000 plt.arrow start and end locations are wrong · Issue #12768 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

plt.arrow start and end locations are wrong #12768

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Austrianguy opened this issue Nov 7, 2018 · 6 comments
Open

plt.arrow start and end locations are wrong #12768

Austrianguy opened this issue Nov 7, 2018 · 6 comments
Labels
Documentation keep Items to be ignored by the “Stale” Github Action topic: arrow

Comments

@Austrianguy
Copy link

Bug report

Bug summary

When I draw an arrow from a point (x, y) with offset (dx, dy), then I expect the arrow to start at (x, y) and point to (x+dx, y+dy). But that's not what happens.

Code for reproduction

import matplotlib.pyplot as plt

plt.text(0, 2.1, 'head_starts_at_zero=False')
plt.arrow(0, 2, 1, 0, head_width=0.05, head_starts_at_zero=False)

plt.arrow(0, 1, 1, 0, head_width=0.05, head_starts_at_zero=True)
plt.text(0, 0.8, 'head_starts_at_zero=True')

plt.vlines([0, 1], 0, 3)
plt.axis([-1, 2, 0, 3])

Actual outcome

With head_starts_at_zero=False the arrow is longer than specified by the head_length (possibly intended)
With head_starts_at_zero=True the arrow is the same length as before but moved along its direction by an arbitrary distance (possibly the head_width).

printscreen

Expected outcome

The default option should be to have the arrow start from the start coordinates and the arrow tip ending at the end coordinates. It might be nice to also have the option where the base of the arrow is at the end coordinates and the arrow head goes further (like the upper example in the plot).

Besides this feature working wrong, the documentation is unclear, which made it hard for me to find this bug:

head_starts_at_zero: bool (default: False)
if True, the head starts being drawn at coordinate 0 instead of ending at coordinate 0.

What's "coordinate 0" here?

Might be better to write:

head_starts_at_zero: bool (default: False)
if True, the head starts being drawn at the specified end point instead of ending at the end point.

if that's the intended behaviour.

Matplotlib version

  • Operating system: Windows 10
  • Matplotlib version: 2.2.3
  • Matplotlib backend (print(matplotlib.get_backend())): Qt5Agg
  • Python version: 2.7
  • Jupyter version (if applicable): ---
  • Other libraries: ---

Environment built from the Anaconda default channel.

@ImportanceOfBeingErnest
Copy link
Member

The feature you're looking for, namely the arrow starting from the start coordinates and the arrow tip ending at the end coordinates, is available via the

length_includes_head: bool (default: False)
True if head is to be counted in calculating the length.

option.

plt.arrow(0, 2, 1, 0, head_width=0.05, length_includes_head=True)

image

Are you asking for the default False to be changed to True?
Personally, I'm not a big fan of changing defaults, simply because it'll change existent code's outcome. But if there is some special reason for it, it can sure be discussed.


Concerning the head_starts_at_zero option, this will indeed shift the arrow by half the head_length

if head_starts_at_zero:
left_half_arrow += [head_length / 2, 0]

A usecase is seen in this example, where two arrows below one another, and shifted by half a head length, result in a picture like this:

image

I do have the feeling that this might be about the only usecase of this; and even then it's questionable in how far this needs to be an argument to the function. Since arrow operates in data coordinates throughout, it's probably easier to manually add half the number entered for the head length.

I do however agree that the docstring is not useful at all. It might be best to just describe what the argument does,

Whether or not to shift the arrow by half the head_length in the direction it points to.

I'm for now adding this issue to the list of things to consider for a tutorial about arrows (#11350).

@Austrianguy
Copy link
Author

Not sure how I missed length_includes_head. That one is easy to find.

Indeed I think the default length_includes_head=False is a poor choice. I would think most people expect the opposite. But I do understand that it's problematic to switch defaults. A few use cases where the arrow length is usually from tail to tip:

  1. Using an arrow to indicate a vector: the convention in science and engineering is that the vector's magnitude is from tail to tip. That's also in line with how our brain perceives the size of an arrow.
  2. Using an arrow to point at something: You often want the tip pointing exactly at the target or set a little back. You definitely never want the base of the arrow head at the target, and the tip overshooting.
  3. not a use case but if drawing an arrow in Word, Photoshop, Paint etc. etc. you also draw it from tail to tip. So people are used to that.

I think that covers most use cases that arrows are used for..

So yea the only thing that's left is clearing up the doc for head_starts_at_zero. The name of the kwarg doesn't capture at all what it does. Your suggestion for the doc sentence looks perfectly clear to me.

On another note, the use case above doesn't require that kwarg. With the default length_includes_head=False (and head_starts_at_zero=False), it would look like that anyways (just slightly longer). Not sure why one would wanna anchor those arrows to the middle of the arrow head.

@ImportanceOfBeingErnest
Copy link
Member

Well, plt.arrow isn't really the recommended way to plot arrows anyways I'd say. The easiest alternative, which I think it also mentionned in the docs is to use annotate, just without text.

plt.annotate("", xy=(1,2.2), xytext=(0,2.2), arrowprops=dict(facecolor='black'))

or

plt.quiver(0, 2.4, 1, 0, angles='xy', scale_units='xy', scale=1)

Admitedly, the API for those ones is not too clear either. But they would at least cover the above mentionned usecases.

@suuuehgi
Copy link
suuuehgi commented May 12, 2022

Well, plt.arrow isn't really the recommended way to plot arrows anyways I'd say.
?!

Using plt.annotate to draw arrows is a hack.

Meanwhile its matplotlib version 3.5.1 and the arrows still seem to be off:

import numpy as np
import matplotlib.pyplot as plt

Z = np.random.random((50,50))

fig, ax = plt.subplots(figsize=(5.9,5.9))

im = ax.imshow(Z, origin='lower', cmap='coolwarm')

ax.hlines( 10 , 0, Z.shape[0]-1, color='black', ls='-', lw=1, alpha=.3)
ax.hlines( 40 , 0, Z.shape[0]-1, color='black', ls='-', lw=1, alpha=.3)

ax.arrow( 10, 10, 0, 30, color='white', head_width=1, length_includes_head=True)
ax.quiver(20, 10, 0, 30, angles='xy', scale_units='xy', scale=1)

ax.grid(visible=False)
fig.savefig('arrows.jpg', dpi=300)

The head overshoots considerably ... (white: plt.arrow, black: plt.quiver)

head

... and the tail is actually off as well.

tail


EDIT Setting linewidth=0., width=0.3 fixes it.

@tacaswell
Copy link
Member

@suuuehgi Please see the discussion in #22435

@github-actions
Copy link

This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!

@github-actions github-actions bot added the status: inactive Marked by the “Stale” Github Action label May 22, 2023
@story645 story645 added topic: arrow keep Items to be ignored by the “Stale” Github Action and removed status: inactive Marked by the “Stale” Github Action labels May 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Documentation keep Items to be ignored by the “Stale” Github Action topic: arrow
Projects
None yet
Development

No branches or pull requests

5 participants
0