Skip to content

Fix tick marker mirroring (matplotlib conversion) #5310

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions plotly/matplotlylib/mpltools.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,15 +266,12 @@ def get_axes_bounds(fig):
return (x_min, x_max), (y_min, y_max)


def get_axis_mirror(main_spine, mirror_spine):
if main_spine and mirror_spine:
def get_axis_mirror(main_spine, mirror_spine, main_tick_markers, mirror_tick_markers):
if main_spine and mirror_spine and main_tick_markers and mirror_tick_markers:
return True
if main_tick_markers and mirror_tick_markers:
return "ticks"
elif main_spine and not mirror_spine:
return False
elif not main_spine and mirror_spine:
return False # can't handle this case yet!
else:
return False # nuttin'!
return False


def get_bar_gap(bar_starts, bar_ends, tol=1e-10):
Expand Down
12 changes: 10 additions & 2 deletions plotly/matplotlylib/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,16 @@ def open_axes(self, ax, props):
top_spine = mpltools.get_spine_visible(ax, "top")
left_spine = mpltools.get_spine_visible(ax, "left")
right_spine = mpltools.get_spine_visible(ax, "right")
xaxis["mirror"] = mpltools.get_axis_mirror(bottom_spine, top_spine)
yaxis["mirror"] = mpltools.get_axis_mirror(left_spine, right_spine)
bottom_tick_markers = ax.xaxis.get_tick_params()["bottom"]
top_tick_markers = ax.xaxis.get_tick_params()["top"]
left_tick_markers = ax.yaxis.get_tick_params()["left"]
right_tick_markers = ax.yaxis.get_tick_params()["right"]
xaxis["mirror"] = mpltools.get_axis_mirror(
bottom_spine, top_spine, bottom_tick_markers, top_tick_markers
)
yaxis["mirror"] = mpltools.get_axis_mirror(
left_spine, right_spine, left_tick_markers, right_tick_markers
)
xaxis["showline"] = bottom_spine
yaxis["showline"] = top_spine

Expand Down
4 changes: 4 additions & 0 deletions plotly/matplotlylib/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
75 changes: 75 additions & 0 deletions plotly/matplotlylib/tests/test_renderer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import plotly.tools as tls

from . import plt

def test_axis_mirror_with_spines_and_ticks():
"""Test that mirror=True when both spines and ticks are visible on both sides."""
fig, ax = plt.subplots()
ax.plot([0, 1], [0, 1])

# Show all spines
ax.spines['top'].set_visible(True)
ax.spines['bottom'].set_visible(True)
ax.spines['left'].set_visible(True)
ax.spines['right'].set_visible(True)

# Show ticks on all sides
ax.tick_params(top=True, bottom=True, left=True, right=True)

plotly_fig = tls.mpl_to_plotly(fig)

assert plotly_fig.layout.xaxis.mirror == True
assert plotly_fig.layout.yaxis.mirror == True


def test_axis_mirror_with_ticks_only():
"""Test that mirror='ticks' when only ticks are visible on both sides."""
fig, ax = plt.subplots()
ax.plot([0, 1], [0, 1])

# Hide opposite spines
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)

# Show ticks on all sides
ax.tick_params(top=True, bottom=True, left=True, right=True)

plotly_fig = tls.mpl_to_plotly(fig)

assert plotly_fig.layout.xaxis.mirror == "ticks"
assert plotly_fig.layout.yaxis.mirror == "ticks"


def test_axis_mirror_false_with_one_sided_ticks():
"""Test that mirror=False when ticks are only on one side."""
fig, ax = plt.subplots()
ax.plot([0, 1], [0, 1])

# Default matplotlib behavior - ticks only on bottom and left
ax.tick_params(top=False, bottom=True, left=True, right=False)

plotly_fig = tls.mpl_to_plotly(fig)

assert plotly_fig.layout.xaxis.mirror == False
assert plotly_fig.layout.yaxis.mirror == False


def test_axis_mirror_mixed_configurations():
"""Test different configurations for x and y axes."""
fig, ax = plt.subplots()
ax.plot([0, 1], [0, 1])

# X-axis: spines and ticks on both sides (mirror=True)
ax.spines['top'].set_visible(True)
ax.spines['bottom'].set_visible(True)
ax.tick_params(top=True, bottom=True)

# Y-axis: only ticks on both sides (mirror='ticks')
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(True)
ax.tick_params(left=True, right=True)

plotly_fig = tls.mpl_to_plotly(fig)

assert plotly_fig.layout.xaxis.mirror == True
assert plotly_fig.layout.yaxis.mirror == "ticks"