Skip to content

[Bug]: Wrong 3D plot #27823

@renatobellotti

Description

@renatobellotti

Bug summary

Using vertical_axis="y" in ax.view_init() results in wrong plot.

Code for reproduction

import numpy as np
import matplotlib.pyplot as plt


def cuboid_data(center, extent):
    # Implementation is based on the following StackOverflow posts:
    # https://stackoverflow.com/a/49281004
    # https://stackoverflow.com/a/35978146

    # suppose axis direction: x: to left; y: to inside; z: to upper
    # get the (left, outside, bottom) point
    o = center
    # get the length, width, and height
    l, w, h = extent
    x = [[o[0], o[0] + l, o[0] + l, o[0], o[0]],  # x coordinate of points in bottom surface
         [o[0], o[0] + l, o[0] + l, o[0], o[0]],  # x coordinate of points in upper surface
         [o[0], o[0] + l, o[0] + l, o[0], o[0]],  # x coordinate of points in outside surface
         [o[0], o[0] + l, o[0] + l, o[0], o[0]]]  # x coordinate of points in inside surface
    y = [[o[1], o[1], o[1] + w, o[1] + w, o[1]],  # y coordinate of points in bottom surface
         [o[1], o[1], o[1] + w, o[1] + w, o[1]],  # y coordinate of points in upper surface
         [o[1], o[1], o[1], o[1], o[1]],          # y coordinate of points in outside surface
         [o[1] + w, o[1] + w, o[1] + w, o[1] + w, o[1] + w]]    # y coordinate of points in inside surface
    z = [[o[2], o[2], o[2], o[2], o[2]],                        # z coordinate of points in bottom surface
         [o[2] + h, o[2] + h, o[2] + h, o[2] + h, o[2] + h],    # z coordinate of points in upper surface
         [o[2], o[2], o[2] + h, o[2] + h, o[2]],                # z coordinate of points in outside surface
         [o[2], o[2], o[2] + h, o[2] + h, o[2]]]                # z coordinate of points in inside surface
    
    return np.asarray(x), np.asarray(y), np.asarray(z)


def annotate_patient_axes(ax):
    xmin, xmax = ax.get_xlim()
    ax.set_xticks((xmin, xmax))
    ax.set_xticklabels(("R", "L"))
    ax.set_xlabel("x", fontsize=20)

    ymin, ymax = ax.get_ylim()
    ax.set_yticks((ymin, ymax))
    ax.set_yticklabels(("A", "P"))
    ax.set_ylabel("y", fontsize=20)

    zmin, zmax = ax.get_zlim()
    ax.set_zticks((zmin, zmax))
    ax.set_zticklabels(("I", "S"))
    ax.set_zlabel("z", fontsize=20)


def plot_patient(ax):
    # start position, extent, color
    parts = {
        "right_arm": ((0, 1, 12), (1, 1, 9), "gray"),
        "left_arm": ((7, 1, 12), (1, 1, 9), "gray"),
        "body": ((1, 0, 9), (6, 3, 12), "blue"),
        "left_leg": ((2, 1, 0), (1, 1, 9), "gray"),
        "right_leg": ((5, 1, 0), (1, 1, 9), "gray"),
        "head": ((2, 1, 21), (4, 1, 5), "orange"),
        "nose": ((3.5, 1, 22), (1, -2, 1), "red"),
    }
    for val in parts.values():
        start, extent, color = val
        X, Y, Z = cuboid_data(start, extent);
        ax.plot_surface(X, Y, Z, color=color, edgecolor="black")

    ax.set_box_aspect((1, 1, 1))

    # Axis labels.
    ax.set_xlim((-5, 14)) # Patient occupies (0, 9) in x.
    ax.set_ylim((-6,  8)) # Patient occupies (-1, 3) in y.
    ax.set_zlim((-5, 29)) # Patient occupies (0, 24) in z.

    annotate_patient_axes(ax)


def plot_patient_perspectives():
    fig = plt.figure(figsize=(2*8, 2*4.5))
    ax = fig.add_subplot(221, projection="3d")
    ax2 = fig.add_subplot(222, projection="3d")
    ax3 = fig.add_subplot(223, projection="3d")
    ax4 = fig.add_subplot(224, projection="3d")

    plot_patient(ax)
    plot_patient(ax2)
    plot_patient(ax3)
    plot_patient(ax4)

    # See documentation for meaning of angles:
    # https://matplotlib.org/stable/api/_as_gen/mpl_toolkits.mplot3d.axes3d.Axes3D.view_init.html

    # Top view on patient.
    ax.set_title("Top view", fontsize=20)
    ax.view_init(-90, 0, vertical_axis="y")

    # Side view on patient.
    ax2.set_title("Side view", fontsize=20)
    ax2.view_init(180, -90, vertical_axis="y")

    # Foot view on patient.
    ax3.set_title("Foot view", fontsize=20)
    ax3.view_init(180, 0, vertical_axis="y")
    
    # Perspective view on patient.
    ax4.set_title("Perspective view", fontsize=20)
    ax4.view_init(210, -70, vertical_axis="y")

    fig.tight_layout()

    return fig, [ax, ax2, ax3, ax4]


fig, axes = plot_patient_perspectives();

Actual outcome

The bottom right plot misplaces the objects in 3D space and is not consistent with the axis-aligned projections.

Expected outcome

Consistent views.

Additional information

No response

Operating system

Linux

Matplotlib Version

3.8.3

Matplotlib Backend

module://matplotlib_inline.backend_inline

Python version

3.9.18

Jupyter version

No response

Installation

conda

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions