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

Code before:
        assert offset_bbox is not None

        # The tight bbox should fully contain the offset text bbox
        # Check that offset_bbox is within bbox bounds (with small tolerance for
        # floating point errors)
        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(monkeypatch):
    fig = plt.figure()
    ax = fig.add_subplot(projection="3d")
    fig.canvas.draw()

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

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

    press_event = MouseEvent(
        "button_press_event", fig.canvas,
        x=100, y=100, button=1,
    )
    press_event.inaxes = ax
    ax._button_press(press_event)

    ax._button_pressed = 1
    ax._rotate_btn = [1]
    ax._sx, ax._sy = press_event.x, press_event.y

    move_event = MouseEvent(
        "motion_notify_event", fig.canvas,
        x=120, y=120, button=1, key="control",
    )
    move_event.inaxes = ax
    move_event.button = 1
    ax._on_move(move_event)

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

    assert ax.elev == pytest.approx(step * round(ax.elev / step))
    assert ax.azim == pytest.approx(step * round(ax.azim / step))
    assert ax.roll == pytest.approx(step * round(ax.roll / step))

Review comment (bug):
You'll have to do some debugging if it doesn't work. Does it work if you remove these lines and set the default to 5.0? (I don't think 0.0 makes sense as the default since it disables this behavior)

Code after:
        assert offset_bbox is not None

        # The tight bbox should fully contain the offset text bbox
        # Check that offset_bbox is within bbox bounds (with small tolerance for
        # floating point errors)
        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'}):