Skip to content

[Bug]: class Axes3D.set_box_aspect() sets wrong aspect ratios when Axes3D.view_init( vertical_axis='y') is enabled. #24328

Closed
@Michallote

Description

@Michallote

Bug summary

Whenever creating a 3D plot with vertical_axis = 'y' option the set_box_aspect() method scales the axes incorrectly. The 3-tuple is expected to behave as x:y:z, instead it behaves like z:x:y

Code for reproduction

"""
Created on Mon Oct 31 18:30:31 2022

@author: Michel Gordillo
"""

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

fig = plt.figure(num=4, clear=True)
ax = fig.add_subplot(1, 1, 1, projection='3d')
ax.view_init(vertical_axis='y')

(theta, phi) = np.meshgrid(np.linspace(0, 2 * np.pi, 41),
                           np.linspace(0, 2 * np.pi, 41))

x = (3 + np.cos(phi)) * np.cos(theta)
z = (3 + np.cos(phi)) * np.sin(theta)
y = np.sin(phi)

k = 4

xlim = [-k, k]  #2k
ylim = [-k/2, k/2] #k
zlim = [-k, k] #2k

box_aspect = (4,2,4) #(x,y,z)

def fun(t, f): return (np.cos(f + 2 * t) + 1) / 2


dplot = ax.plot_surface(x, y, z, facecolors=cm.jet(fun(theta, phi)))
ax.set(xlabel='x', 
       ylabel='y', 
       zlabel='z',
       xlim = xlim,
       ylim = ylim,
       zlim = zlim,
       # xticks = [-4, -2, 2, 4],
       # yticks = [-4, -2, 2, 4],
       # zticks = [-1, 0, 1],
       title='Donut!')

ax.set_box_aspect(aspect = box_aspect)

fig.tight_layout()

Actual outcome

Matplotlib_bug_axes_vertical_y

The box_aspect tuple:
box_aspect = (4,2,4) #(x,y,z)

Actually the axis scaled as
box_aspect = (4,2,4) -> #(z,x,y)
Like if shifting y to the right.

Expected outcome

Matplotlib_bug_fix_axes_vertical_y

To produce this result i just applied the same logic in reverse:
box_aspect = (4,4,2) #(z,x,y) <- Expecting the outcome to be applied like this

And it actually does.

Additional information

This only happens as explained in the title when both vertical = y option is enabled and we try to manually scale_box_aspect(). The problem is probably from how was vertical axis selection actually implemented.

From the documentation:

set_box_aspect(aspect, *, zoom=1)
[...]
aspect3-tuple of floats or None
Changes the physical dimensions of the Axes3D, such that the ratio of the axis lengths in display units is x:y:z.

This behaviour is not respected when vertical_axis = 'y' in enabled. The behaviour appears to be: z:x:y instead.

To actually fix this I just created a tuple with the new definition in mind, this could just be patched by shifting the tuple to make the vertical axis be the last element of the tuple

(x,y,z) -> input tuple

vertical axis == 'z' -> (x,y,z) no problem
vertical axis == 'y' -> (z,x,y) rotate right the tuple
vertical axis == 'x' -> (y,z,x) rotate right twice the tuple

Operating system

Windows

Matplotlib Version

'3.6.1'

Matplotlib Backend

Qt5Agg

Python version

Python 3.8.5

Jupyter version

Spyder 4.1.5

Installation

conda

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions