Skip to content
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

[Bug]: Clipping mask can shift in PDF and SVG file outputs when Bbox is adjusted #24890

Closed
michaelmarty opened this issue Jan 5, 2023 · 6 comments · Fixed by #24894
Closed
Milestone

Comments

@michaelmarty
Copy link

Bug summary

I am trying to adjust the clipping box around markers, which work great for WxAgg and for PNG output. However, when I export in SVG or PDF, the clipping box has shifted up and to the right.

Code for reproduction

import matplotlib.pyplot as plt
from matplotlib.transforms import Bbox
from copy import deepcopy

# Generic plot
plt.plot(range(6),range(6), marker='o')
plt.xlim(0, 5)
plt.ylim(0, 5)

ax = plt.gca()
# Original Code to expand the clipping box
# oldpts = ax.bbox.get_points()
# oldpts[1, 1] *= 1.05
# newbbox = Bbox(oldpts)
# Simple code to just copy the old bounding box
newbbox = deepcopy(ax.bbox)
for o in ax.lines:
    o.set_clip_box(newbbox)

# Save SVG
plt.savefig("test.svg")
# Show plot
plt.show()

Actual outcome

See figure. The 0,0 point is clipped and the 5,5 point is not.

test2

Expected outcome

See figure. Both should be clipped. This is what we see with PNG output and with the plt.show() rendering.

test2

Additional information

When I copy the Bbox and set that as the clip box, it works fine. But, when I deepcopy it, it doesn't.

I opened the SVG file in Illustrator, and there is a clipping box that is shifted up and to the right of the origin. It can be deleted in Illustrator to recover the content below.

I'm definitely open to workarounds. I'm not sure if I'm doing this right, but it seems to work for PNG files, which makes me think there is a glitch in the background somewhere.

Operating system

Windows

Matplotlib Version

3.6.2

Matplotlib Backend

module://backend_interagg

Python version

3.10.8

Jupyter version

No response

Installation

pip

@tacaswell
Copy link
Member

This is like because the axes clip box is not actually fixed, it depends on the figure size and resolution. The default dpi is 100 (and the target screen units are 'pixels'), but for the vector output the effect dpi is 72 and the target units is pt. When you make the copy (or otherwise get a static bounding box) it will come out wrong.

The hacky option is to create the figure at 72 DPI and then you will "get lucky", a more robust solution is to make a transformed box from [-.05, 1.05, -0.5, 1.05] in the transAxes coordinate system (75% chance I have spelled how to get that wrong), and the fanciest would be to use the transform chain to expand the bbox in a way that will follow the axes.

https://matplotlib.org/stable/tutorials/advanced/transforms_tutorial.html is probably the reference you want.

@michaelmarty
Copy link
Author

That makes sense. Any tips on how to do make a transformed box in the transAxes coordinate system? I've been searching and experimenting and can't get it, even with tips from the tutorial. I've also tried to modify the ax.bbox in place, but it won't allow that.

@jklymak
Copy link
Member

jklymak commented Jan 6, 2023

I'm a little confused about what you are trying to do. If you remove the deepcopy, the default clipping is as you say you want it. So, what is your actual desired effect that you are messing with the clip box for?

@jklymak
Copy link
Member

jklymak commented Jan 6, 2023

I think what you are trying to do is simply:

newbbox = TransformedBbox(Bbox([[0, 0], [1.05, 1.05]]),
                          ax.transAxes)

But, I could be misinterpreting. I actually would agree that set_clippath could be a lot more clear about how to make these boxes.

@tacaswell
Copy link
Member

import matplotlib.pyplot as plt
from matplotlib.transforms import Bbox, TransformedBbox

fig, ax = plt.subplots()
bb = TransformedBbox(Bbox([[-.05, -.05], [1.05, 1.05]]), ax.transAxes)
print(bb.get_points())
fig.set_size_inches(5, 5)
print(bb.get_points())

@michaelmarty
Copy link
Author

That's it! Thanks @jklymak and @tacaswell! Yeah, I can usually figure out how to get MPL to do what I'm looking for, but this one has stumped me for years. Finally thought I got it and then discovered this new issue. Thanks for your help!

@QuLogic QuLogic added this to the v3.7.0 milestone Jan 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants