Skip to content

Convert axhline, axvline, and axline to layout shapes - #5719

Open
robertoffmoura wants to merge 13 commits into
plotly:mainfrom
robertoffmoura:rm/support-axhline
Open

robertoffmoura wants to merge 13 commits into
plotly:mainfrom
robertoffmoura:rm/support-axhline

Conversation

@robertoffmoura

Copy link
Copy Markdown
Contributor

mpl_to_plotly silently drops reference lines. axhline, axvline, and axline produce lines with blended or axes transforms, which the renderer skipped with "I found a line that didn't have 'data' coordinates! I will not draw it, but I did not crash.". The converted figure ends up with the reference lines missing completely.

Fix: reference lines are now drawn as layout line shapes:

  • endpoints are mapped to data coordinates using ax.transData.inverted()
  • added as go.layout.Shape(type="line") on the current subplot
  • color, alpha, linewidth, and dash styles are preserved

Before: 0 shapes (lines dropped with a warning).
After: lines render as layout shapes spanning the axis limits.

Snippet to reproduce:

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import plotly.tools as tls

cases = {
    "axhline": lambda: plt.axhline(0.5, color="red", linestyle="--"),
    "axvline": lambda: plt.axvline(2.0, color="blue", linestyle="-"),
    "axline":  lambda: plt.axline((1, 0.5), slope=0.5, color="green", linestyle="-."),
}

for name, build in cases.items():
    fig, ax = plt.subplots()
    ax.set_xlim(0, 4)
    ax.set_ylim(0, 2)
    build()

    p = tls.mpl_to_plotly(fig)  # 0 shapes before this fix
    print(f"{name:10s} -> {len(p.layout.shapes)} shape(s)")
    p.write_image(f"{name}_plotly.png")
axhline axline axvline
axhline_plotly axline_plotly axvline_plotly

@camdecoster camdecoster left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for putting this together! There are a couple of issues to address, but it's an improvement. I left one suggestion about axis refs that isn't required, but it's worth looking at for this PR.

Could you please add a CHANGELOG entry?

Comment thread plotly/matplotlylib/renderer.py Outdated
Comment thread plotly/matplotlylib/renderer.py Outdated
Comment thread plotly/matplotlylib/renderer.py
Comment thread plotly/matplotlylib/renderer.py Outdated
Comment on lines +502 to +503
xref="x{0}".format(self.axis_ct),
yref="y{0}".format(self.axis_ct),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works as long as the user doesn't zoom/pan the chart. Once they do, the lines no longer span the chart. You could update these to use "x domain" and "y domain" for the proper lines.

Image

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the horizontal and vertical lines to use domain, but the diagonal ones are trickier. If we use domain for the diagonal lines, we'd get incorrect behaviour when panning or zooming in only one dimension. A workaround for the diagonal lines is to scale them by some factor (e.g. by 100). Not ideal, but I don't think Plotly supports infinite diagonal lines. Do you have any suggestions?

@robertoffmoura

Copy link
Copy Markdown
Contributor Author

Thanks! I added the changelog entry, addressed your suggestions, and left one comment regarding the zoom/pan issue for axline.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants