Repository: matplotlib/matplotlib
File: lib/mpl_toolkits/mplot3d/tests/test_axes3d.py (Python)

Code before:
        assert bbox.x0 <= offset_bbox.x0 + 1e-6, \
            f"bbox.x0 ({bbox.x0}) should be <= offset_bbox.x0 ({offset_bbox.x0})"
        assert bbox.y0 <= offset_bbox.y0 + 1e-6, \
            f"bbox.y0 ({bbox.y0}) should be <= offset_bbox.y0 ({offset_bbox.y0})"
        assert bbox.x1 >= offset_bbox.x1 - 1e-6, \
            f"bbox.x1 ({bbox.x1}) should be >= offset_bbox.x1 ({offset_bbox.x1})"
        assert bbox.y1 >= offset_bbox.y1 - 1e-6, \
            f"bbox.y1 ({bbox.y1}) should be >= offset_bbox.y1 ({offset_bbox.y1})"


class DummyEvent:
    def __init__(self, xdata=None, ydata=None, inaxes=None):
        self.xdata = xdata
        self.ydata = ydata
        self.inaxes = inaxes
        self.x = 0
        self.y = 0
        self.button = 1
        self.key = "control"


def _is_multiple_of_step(val, step):
    return np.isclose(val, step * round(val / step), atol=5e-2)


def test_ctrl_rotation_snaps_to_5deg(monkeypatch):
    fig = plt.figure()
    ax = fig.add_subplot(projection="3d")

    ax.M = np.eye(4)
    ax._pseudo_w = 1
    ax._pseudo_h = 1

    ax.button_pressed = 1
    ax._rotate_btn = [1]
    ax._sx, ax._sy = 0.0, 0.0

    ax.elev = 12.3
    ax.azim = 33.7
    ax.roll = 2.2

    monkeypatch.setitem(
        plt.rcParams,
        "axes3d.snap_rotation",
        5,
    )

    monkeypatch.setitem(plt.rcParams, "axes3d.mouserotationstyle", "azel")

    captured = {}


Review comment (suggestion):
`test_toolbar_zoom_pan` might be a useful reference here (though it's probably a bit more complicated than what you need)

Code after:
        assert bbox.x0 <= offset_bbox.x0 + 1e-6, \
            f"bbox.x0 ({bbox.x0}) should be <= offset_bbox.x0 ({offset_bbox.x0})"
        assert bbox.y0 <= offset_bbox.y0 + 1e-6, \
            f"bbox.y0 ({bbox.y0}) should be <= offset_bbox.y0 ({offset_bbox.y0})"
        assert bbox.x1 >= offset_bbox.x1 - 1e-6, \
            f"bbox.x1 ({bbox.x1}) should be >= offset_bbox.x1 ({offset_bbox.x1})"
        assert bbox.y1 >= offset_bbox.y1 - 1e-6, \
            f"bbox.y1 ({bbox.y1}) should be >= offset_bbox.y1 ({offset_bbox.y1})"


def test_ctrl_rotation_snaps_to_5deg():
    fig = plt.figure()
    ax = fig.add_subplot(projection='3d')

    initial = (12.3, 33.7, 2.2)
    ax.view_init(*initial)
    fig.canvas.draw()

    s = 0.25
    step = plt.rcParams["axes3d.snap_rotation"]

    # First rotation without Ctrl
    with mpl.rc_context({'axes3d.mouserotationstyle': 'azel'}):
        MouseEvent._from_ax_coords(
            "button_press_event", ax, (0, 0), MouseButton.LEFT
        )._process()

        MouseEvent._from_ax_coords(
            "motion_notify_event",
            ax,
            (s * ax._pseudo_w, s * ax._pseudo_h),
            MouseButton.LEFT,
        )._process()

    fig.canvas.draw()

    rotated_elev = ax.elev
    rotated_azim = ax.azim
    rotated_roll = ax.roll

    # Reset before ctrl rotation
    ax.view_init(*initial)
    fig.canvas.draw()

    # Now rotate with Ctrl
    with mpl.rc_context({'axes3d.mouserotationstyle': 'azel'}):
        MouseEvent._from_ax_coords(
            "button_press_event", ax, (0, 0), MouseButton.LEFT
        )._process()

        MouseEvent._from_ax_coords(