-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Fix mplot3d projection #8896
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
Fix mplot3d projection #8896
Changes from all commits
64f197b
c765493
58e961d
c95fea3
c641607
7f1732e
e601965
0848bd1
0492b88
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
``Axes3D`` no longer distorts the 3d plot to match the 2d aspect ratio | ||
---------------------------------------------------------------------- | ||
|
||
Plots made with :class:`~mpl_toolkits.mplot3d.axes3d.Axes3D` were previously | ||
stretched to fit a square bounding box. As this stretching was done after | ||
the projection from 3D to 2D, it resulted in distorted images if non-square | ||
bounding boxes were used. | ||
|
||
As of this release, this no longer occurs. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -257,6 +257,20 @@ def tunit_edges(self, vals=None, M=None): | |
(tc[7], tc[4])] | ||
return edges | ||
|
||
def apply_aspect(self, position=None): | ||
if position is None: | ||
position = self.get_position(original=True) | ||
|
||
# in the superclass, we would go through and actually deal with axis | ||
# scales and box/datalim. Those are all irrelevant - all we need to do | ||
# is make sure our coordinate system is square. | ||
figW, figH = self.get_figure().get_size_inches() | ||
fig_aspect = figH / figW | ||
box_aspect = 1 | ||
pb = position.frozen() | ||
pb1 = pb.shrunk_to_aspect(box_aspect, pb, fig_aspect) | ||
self.set_position(pb1.anchored(self.get_anchor(), pb), 'active') | ||
|
||
@artist.allow_rasterization | ||
def draw(self, renderer): | ||
# draw the background patch | ||
|
@@ -966,6 +980,9 @@ def get_proj(self): | |
|
||
dist is the distance of the eye viewing point from the object point. | ||
""" | ||
# chosen for similarity with the initial view before gh-8896 | ||
pb_aspect = np.array([4, 4, 3]) / 3.5 | ||
eric-wieser marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+983
to
+984
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I imagine that there are going to be some requests for an API to change this, for people who liked their 1:1:1 plot boxes. I started working on that two years ago, but it opened a can of worms that I didn't want to open concurrently with this PR. At some point I'll see if I can rebase those changes. |
||
|
||
relev, razim = np.pi * self.elev/180, np.pi * self.azim/180 | ||
|
||
xmin, xmax = self.get_xlim3d() | ||
|
@@ -975,10 +992,10 @@ def get_proj(self): | |
# transform to uniform world coordinates 0-1, 0-1, 0-1 | ||
worldM = proj3d.world_transformation(xmin, xmax, | ||
ymin, ymax, | ||
zmin, zmax) | ||
zmin, zmax, pb_aspect=pb_aspect) | ||
|
||
# look into the middle of the new coordinates | ||
R = np.array([0.5, 0.5, 0.5]) | ||
R = pb_aspect / 2 | ||
|
||
xp = R[0] + np.cos(razim) * np.cos(relev) * self.dist | ||
yp = R[1] + np.sin(razim) * np.cos(relev) * self.dist | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use the figure's aspect ratio? What if the figure has 3 subplots all in a row?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because all sizes in matplotlib are relative to the figure.
pb.shrunk_to_aspect
seems to take the ratio of the top-level parent (the figure), and the desired ratio. This is because the end result is the size relative to the parent, as all objects in matplotlib seem to be sized - so to ensure a given aspect ratio, you need to account for that of the parentNote that this is the same as the normal aspect=equal code, so is presumably correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ping @efiring, as I think you are the resident expert on all the ways this could possibly go wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes sense. I have no intention of spending the time required to understand mplot3d well enough to make a detailed review, but in the absence of any such review I am inclined to trust that this PR is a major improvement, as it appears to be fixing a basic design flaw.