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

Skip to content

31537 text wrap figure edge#31546

Open
FazeelUsmani wants to merge 2 commits intomatplotlib:mainfrom
FazeelUsmani:31537-text-wrap-figure-edge
Open

31537 text wrap figure edge#31546
FazeelUsmani wants to merge 2 commits intomatplotlib:mainfrom
FazeelUsmani:31537-text-wrap-figure-edge

Conversation

@FazeelUsmani
Copy link
Copy Markdown
Contributor

Text placed exactly on a figure edge with an axis-aligned rotation and wrap=True collapses to zero wrap width, producing one word per line. Minimal reproducer from the issue:

  import matplotlib.pyplot as plt
  fig = plt.figure()                                                                                                                                                            
  fig.text(0.0, 1.0, "Helo World! thisisalongassstring aneventextralongassstring",
           verticalalignment="top", horizontalalignment="left",                                                                                                                 
           multialignment="left", wrap=True)                                                                                                                                    
  plt.show()

Root cause is in Text._get_dist_to_box: the trig formula divides by cos(radians(90)) (~6e-17). Away from the edge the degenerate term evaluates to a huge number (non-zero distance over a tiny denominator) and loses min() to the sensible other side. On the edge the numerator is zero too, so the result collapses to zero.

The fix short-circuits the four cardinal rotations and normalizes rotation to [0, 360) with a small tolerance, so that near-cardinal angles coming out of transform_angles (e.g. -90.00000000000003 when transform_rotates_text=True) also take the fast path. Direction follows @rcomer's suggestion in the issue thread.

closes #31537

PR checklist

  • closes [Bug]: Erroneous text wrapping when text vertical position is 1.0 #31537 is in the body of the PR description
  • new and changed code is tested (test_wrap_on_figure_edge, test_wrap_on_figure_edge_transform_rotates_text in lib/matplotlib/tests/test_text.py)
  • [N/A] Plotting related features are demonstrated in an example
  • [N/A] New Features and API Changes are noted with a directive and release note (bugfix only)
  • [N/A] Documentation complies with general and docstring guidelines

Text with wrap=True at an axis-aligned rotation on the matching figure
edge collapsed to zero wrap width. In _get_dist_to_box the trig formula
divides by cos(radians(90)) (~6e-17): normally that term's large value
loses the min() to the sensible other side, but when the position sits
exactly on the edge its numerator is also zero and the returned
distance is zero.

Short-circuit cardinal rotations. Also normalize rotation to [0, 360)
with a small absolute tolerance so near-cardinal angles coming out of
transform_angles (e.g. -90.00000000000003 via transform_rotates_text)
still take the fast path instead of blowing up in the trig formula.

Regression tests cover each cardinal rotation on its broken edge plus
a transform_rotates_text case.
Comment thread lib/matplotlib/text.py Outdated
rotation = rotation % 360
# Short-circuit cardinal angles, otherwise cos(radians(90)) makes
# the trig formula below blow up when the text is on the edge.
tol = 1e-10
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why choose a / exactly this tolerance?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Picked it a few orders of magnitude above the float noise I saw, but with the existing % 360 in get_rotation, the tolerance isn't actually needed. Removing it.

@rcomer
Copy link
Copy Markdown
Member

rcomer commented Apr 22, 2026

All the added tests pass with the simpler patch that I put in the issue. Do you have examples that demonstrate the need for this more complicated approach?

Comment thread lib/matplotlib/text.py Outdated
"""
# Normalize rotation; transform_angles can return values outside
# [0, 360) with tiny float noise.
rotation = rotation % 360
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

AFAICS this is already done within set_rotation or get_rotation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yep, missed that. Dropping the % 360 line.

@FazeelUsmani
Copy link
Copy Markdown
Contributor Author

All the added tests pass with the simpler patch that I put in the issue. Do you have examples that demonstrate the need for this more complicated approach?

You're right, all the tests pass with your original patch. Since get_rotation already does % 360, the transform_angles noise is absorbed before it reaches here. Will simplify.

transform_angles can return a tiny-negative value, and in Python
(tiny_negative) % 360 float-rounds to exactly 360.0, breaking the
[0, 360) range that the docstring promises. Clamp 360 back to 0 so
callers can rely on the documented range.

With that in place, the normalize + tolerance wrapper added to
_get_dist_to_box in the previous commit is no longer needed. Reduce
it to plain cardinal-angle short-circuits, per rcomer's suggestion
in matplotlib#31537.

Rework the transform_rotates_text regression test to compare wrapped
output against a plain rotation=0 reference at the same position,
instead of the weaker "contains a space" assertion.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Erroneous text wrapping when text vertical position is 1.0

3 participants