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

Skip to content

Implement head resizing (and reversal) for larrow/rarrow/darrow #29998

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
wants to merge 21 commits into
base: main
Choose a base branch
from

Conversation

CharlieThornton33
Copy link

@CharlieThornton33 CharlieThornton33 commented May 1, 2025

PR summary

When creating an annotation using boxed text in an arrow shape (patches.BoxStyle.LArrow/RArrow/DArrow), head_width and head_angle arguments may now be passed to adjust the size of the arrow head(s), and (via the use of negative angles) generate 'reversed' arrow heads. Addresses #24618 and is a direct continuation of @Abitamim's work in #24744.

This solves both the issue initially mentioned in #24618 (creating a pentagonal 'road-sign' annotation box), as well as allowing for many other customisation options for arrows; for example, a reversed-head DArrow could act as a way to bring attention to a certain region on an axis.

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(4, 3))

t1 = ax.text(0.5, 0.75, "Road-sign",
            ha="center", va="center", size=15,
            bbox=dict(boxstyle="rarrow,pad=0.3,head_width=1,head_angle=45",
                      fc="lightblue", ec="steelblue", lw=2))

t2 = ax.text(0.5, 0.25, "  Reversed heads   ",
            ha="center", va="center", size=15,
            bbox=dict(boxstyle="darrow,pad=0.3,head_width=1.5,head_angle=-45",
                      fc="lightblue", ec="steelblue", lw=2))

plt.show()

pr_example

A new test, test_boxarrow_adjustment, has been added to test these options.

PR checklist

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for opening your first PR into Matplotlib!

If you have not heard from us in a week or so, please leave a new comment below and that should bring it to our attention. Most of our reviewers are volunteers and sometimes things fall through the cracks.

You can also join us on gitter for real-time discussion.

For details on testing, writing docs, and our review process, please see the developer guide

We strive to be a welcoming and open project. Please follow our Code of Conduct.

@CharlieThornton33
Copy link
Author

Hi everyone,

I was just wondering whether someone would be willing to have a look at this PR? I feel like it's in a complete or nearly-complete state but need some guidance - the two failing CI jobs seem to be:

  • PR cleanliness:
    The added unit test was written first by @Abitamim, and then expanded by myself. When I expanded the test to check the newly-added reversed arrow heads, I changed both the test name (temp_test_boxarrowtest_boxarrow_adjustment) and the name of the output image (roadsign_test_image.pngboxarrow_adjustment_test_image.png) to reflect this. The 'file both added and deleted' error given seems to be a result of adding then deleting roadsign_test_image.png.
  • Python 3.11 on macos-13:
    The failing test here (test_blitting_events) is marked as flaky, with the comment # subprocesses can struggle to get the display, so rerun a few times. I feel it may be that this was just a random instance of the test failing multiple times in a row; unfortunately I don't have access to macos-supported hardware for rerunning this locally, so I was wondering if someone would be able to take a look.

Any help would be greatly appreciated. Thanks!

@timhoffm
Copy link
Member

timhoffm commented May 9, 2025

PR-cleanliness: This is a check so that we don't merge commits with unneeded images, which would increase repo size. You can either squash and force-push. Or ignore this and we'll squash-merge at the end.

Ignore the macos test failure. It's unrelated.

@CharlieThornton33
Copy link
Author

Thank you for getting back so quickly! Would it be okay if you handled the squashing - I'm relatively new to git, and I'm not sure of the best method to use for squashing to keep a relatively clean but traceable commit history?

Copy link
Member

@timhoffm timhoffm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the arrows in the test image, https://github.com/matplotlib/matplotlib/pull/29998/files#diff-ff8adb592a399d60ee5c93fbff7552182f53b74be4573ca21db404ef3c3a6673, the outline sometimes cuts through the text, which must not happen.

From plotting

plt.text(0.5, 0.5, 'text', size=24, bbox=dict(boxstyle="rarrow", fc="w", ec="k"),)
plt.text(0.5, 0.5, 'text', size=24, bbox=dict(boxstyle="square", pad=0, fc="C0", ec='none'))

we can see that the original calculation had a shaft with the length of the text, i.e. the arrow head starts at -pad from the end of the text.

image

This setting was ok for the hard-coded parameters, but when head_size and head_angle can vary, we have to invest more into a good positioning of arrow head.

Comment on lines 65 to 69
angle = -80
angle_step = 120 / repetitions
for i in range(repetitions):
for j, stylename in enumerate(sorted(styles)):
if stylename in ("larrow", "rarrow", "darrow"):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please explain what you are trying to achieve with this rather wild parameter juggling. I cannot follow the logic and the associated reference image looks complicated. I would have expected that you have a set of characteristic cases such as

{'head_width'=1.5, 'head_angle'=90},
{'head_width'=1, 'head_angle'=60},
{'head_witdh'=0.5, 'head_angle'=30},
{'head_witdh'=1.5, 'head_angle'=-90},

for each of 'larrow', 'darrow', 'rarrow'. You could coveniently arrange them on a regular grid.

I don't think you need to test the rotation. By design that's covered by the transform system and is thus orthogonal to the shaping of the arrow. But if you want, you could add an extra entry with rotation in the above matrix.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't aware that the transform system itself was tested elsewhere; I'll remove the rotations, as well as lay the arrows out more regularly. I was rushing quite a bit when writing the test, and I felt the positioning needed to somewhat resemble that in test_boxarrow - the insane double loop is a result of me trying to test multiple cases for each type of arrow while still using the positioning code from that test.

Before I get on to rewriting the test, though, I'll work on the arrow head positioning/padding issue.

@CharlieThornton33
Copy link
Author

Since I'm going to need to make some relatively major changes to the contents of this PR (a redo of the arrow drawing code and a rewrite of its unit test), would it be better for me to amend my last commit (d9b0b32) as suggested in the contributing guide, or make a few new commits? I'm not quite sure how to add more commits to an open PR - do I just push the changes to my development branch?

@rcomer
Copy link
Member

rcomer commented May 10, 2025

@CharlieThornton33 yes you can just push more commits to your current branch and they will appear in this PR. Since you already have several commits they will ultimately need squashing one way or the other (and I see you already asked that we handle that), so I do not think you gain anything from the —amend option.

@CharlieThornton33
Copy link
Author

I've just redone the padding (only for LArrow and RArrow for the moment), and I think the issue with the text escaping the outline has been fixed. I set the arrow body to dynamically increase in length to make sure there's always a gap of at least pad between the edge of the text closest to the arrow head and the position where that edge would first intercept the outline if moved towards the tip:

import numpy as np
import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(3, 8))

t0 = ax.text(0.5, 0.9, "Text",
            ha="center", va="center", rotation=0, size=30,
            bbox=dict(boxstyle="rarrow,pad=0.3,head_width=1.5,head_angle=-90",
                      fc="lightblue", ec="steelblue", lw=2))

t1 = ax.text(0.5, 0.7, "Text",
            ha="center", va="center", rotation=0, size=30,
            bbox=dict(boxstyle="rarrow,pad=0.3,head_width=1.5,head_angle=70",
                      fc="lightblue", ec="steelblue", lw=2))

t2 = ax.text(0.5, 0.5, "Text",
            ha="center", va="center", rotation=0, size=30,
            bbox=dict(boxstyle="rarrow,pad=0.3,head_width=1.5,head_angle=100",
                      fc="lightblue", ec="steelblue", lw=2))

t3 = ax.text(0.5, 0.3, "Text",
            ha="center", va="center", rotation=0, size=30,
            bbox=dict(boxstyle="rarrow,pad=0.3,head_width=1.5,head_angle=130",
                      fc="lightblue", ec="steelblue", lw=2))

t4 = ax.text(0.5, 0.1, "Text",
            ha="center", va="center", rotation=0, size=30,
            bbox=dict(boxstyle="rarrow,pad=0.3,head_width=1.5,head_angle=170",
                      fc="lightblue", ec="steelblue", lw=2))

plt.show()

new_padding

It's also a bit concerning that the CI workflow seems to run on every commit to this PR - am I okay to disable tests in my commit messages until the one where I write the new test to save on CI resources?

@CharlieThornton33
Copy link
Author

I've just noticed - all of the pytest test runs are about to fail because I forgot to remove the flawed test_boxarrow_adjustment test. Is there any way to cancel a CI test run?

@rcomer
Copy link
Member

rcomer commented May 14, 2025

@CharlieThornton33 I wouldn't worry about the CI failing, but if they are still running when you push another commit they will be interrupted and re-started.

@CharlieThornton33
Copy link
Author

Hi,

I'm a bit stuck with how to handle cases where an arrow head extends behind the opposite end of the body; something like

arrow_overflow

I'm not sure whether the arrow head should 'stick out' through the other end of the body, or should cut off at that point.

I'd really appreciate any advice - I don't know if there's any sort of precedent in Matplotlib for this kind of thing.

@timhoffm
Copy link
Member

I don't think there's precedent. While such cases should be rare, my gut feeling is that the tip should not stick out.

@melissawm melissawm moved this from Needs review to Waiting for author in First Time Contributors May 26, 2025
@CharlieThornton33
Copy link
Author

Hi,

I'm afraid I won't be able to finish this PR for a few weeks - I'm currently in exam season at university and I'm having to focus on them at the moment. As soon as I've finished exams (mid June), I'll get back on it and finish the rest of the implementation.

@timhoffm
Copy link
Member

That's ok.

@CharlieThornton33
Copy link
Author

I'm going to try to write a new unit test tomorrow, then hopefully this should be okay to merge.

@CharlieThornton33
Copy link
Author

Hi,

I'm unsure about why the two Windows tests on Azure failed, and I don't think it's likely to be a direct result of my addition of test_boxarrow_adjustment.

The python 3.12 job failed test_fontcache_thread_safe, seemingly by some sort of timeout, and a child process appears to have lingered a bit after bash was stopped.

The python 3.13 job seemed to be slightly confused with versions - it was trying to link a free-threaded version of python at build time during C/C++ compilation (python313t.lib), but the only installed version in this CI job is the non-free-threaded python313.lib.

I'd really appreciate any help/guidance; apart from these failing jobs, I'm quite happy with the feature implementation as it is now and would be really grateful if someone could have a look and see whether it's okay.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Waiting for author
Development

Successfully merging this pull request may close these issues.

[ENH]: "Road sign" boxstyle/annotation
5 participants