FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

plt.arrow start and end locations are wrong · Issue #12768 · matplotlib/matplotlib · GitHub

Repository navigation

plt.arrow start and end locations are wrong #12768

Description

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).

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.

Activity

  1. ImportanceOfBeingErnest commented on Nov 7, 2018

    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)
    

    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:

    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).

  2. Austrianguy commented on Nov 8, 2018

    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.

  3. ImportanceOfBeingErnest commented on Nov 8, 2018

    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.

  4. suuuehgi commented on 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)

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


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

  5. tacaswell commented on May 12, 2022

    Member

    @suuuehgi Please see the discussion in #22435

  6. github-actions commented on May 22, 2023

    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!

  7. added
    keepItems to be ignored by the “Stale” Github Action
    and removed
    status: inactiveMarked by the “Stale” Github Action
    on May 22, 2023
  8. timhoffm commented on Sep 15, 2025

    Member

    I‘m closing this as „won’t fix“. plt.arrow() is broken in so many ways that we should invest Ian’s time in this. Any effort should go towards replacements. For the time being the recommended workaround is plt.annotate().

  9. added and removed
    keepItems to be ignored by the “Stale” Github Action
    on Sep 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL