Skip to content

Add Axes method for drawing infinite lines #9321

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

Closed
wants to merge 2 commits into from
Closed
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
5 changes: 5 additions & 0 deletions doc/users/next_whats_new/2017-12-08-axline.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
New `axline` method
-------------------

A new `~.axes.Axes.axline` method has been added to draw infinitely long lines
that pass through two points.
75 changes: 75 additions & 0 deletions lib/matplotlib/axes/_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,7 @@ def axhline(self, y=0, xmin=0, xmax=1, **kwargs):
--------
hlines : Add horizontal lines in data coordinates.
axhspan : Add a horizontal span (rectangle) across the axis.
axhline : Add a line with an arbitrary slope.

Examples
--------
Expand Down Expand Up @@ -888,6 +889,7 @@ def axvline(self, x=0, ymin=0, ymax=1, **kwargs):
--------
vlines : Add vertical lines in data coordinates.
axvspan : Add a vertical span (rectangle) across the axis.
axline : Add a line with an abritrary slope.
"""

if "transform" in kwargs:
Expand All @@ -908,6 +910,79 @@ def axvline(self, x=0, ymin=0, ymax=1, **kwargs):
self.autoscale_view(scalex=scalex, scaley=False)
return l

@docstring.dedent_interpd
def axline(self, xy1, xy2, **kwargs):
"""
Add an infinitely long straight line that passes through two points.

Parameters
----------
xy1, xy2 : (float, float)
Points for the line to pass through.

Returns
-------
:class:`~matplotlib.lines.Line2D`

Other Parameters
----------------
Valid kwargs are :class:`~matplotlib.lines.Line2D` properties,
with the exception of 'transform':

%(_Line2D_docstr)s

Examples
--------
Draw a thick red line passing through (0, 0) and (1, 1)::

>>> axline((0, 0), (1, 1), linewidth=4, color='r')


See Also
--------
axhline : for horizontal lines
axvline : for vertical lines

Notes
-----
Currently this method does not work properly with non-linear axes.
"""
if not self.get_xscale() == self.get_yscale() == 'linear':
raise NotImplementedError('axline() is only supported on '
'linearly scaled axes')

if "transform" in kwargs:
raise TypeError("'transform' is not allowed as a kwarg; "
"axline generates its own transform.")

x1, y1 = xy1
x2, y2 = xy2
# If x values the same, we have a vertical line
if np.allclose(x1, x2):
if np.allclose(y1, y2):
raise ValueError(
'Cannot draw a line through two identical points '
f'(got x1={x1}, x2={x2}, y1={y1}, y2={y2}).')
line = self.axvline(x1, **kwargs)
return line

slope = (y2 - y1) / (x2 - x1)
intercept = y1 - (slope * x1)

xtrans = mtransforms.BboxTransformTo(self.viewLim)
viewLimT = mtransforms.TransformedBbox(
self.viewLim,
mtransforms.Affine2D().rotate_deg(90).scale(-1, 1))
ytrans = (mtransforms.BboxTransformTo(viewLimT) +
mtransforms.Affine2D().scale(slope).translate(0, intercept))
trans = mtransforms.blended_transform_factory(xtrans, ytrans)

line = mlines.Line2D([0, 1], [0, 1],
transform=trans + self.transData,
**kwargs)
self.add_line(line)
return line

@docstring.dedent_interpd
def axhspan(self, ymin, ymax, xmin=0, xmax=1, **kwargs):
"""
Expand Down
15 changes: 15 additions & 0 deletions lib/matplotlib/tests/test_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3504,6 +3504,21 @@ def test_eb_line_zorder():
ax.set_title("errorbar zorder test")


@check_figures_equal()
def test_axline(fig_test, fig_ref):
ax = fig_test.subplots()
ax.set(xlim=(-1, 1), ylim=(-1, 1))
ax.axline((0, 0), (1, 1))
ax.axline((0, 0), (1, 0), color='C1')
ax.axline((0, 0.5), (1, 0.5), color='C2')

ax = fig_ref.subplots()
ax.set(xlim=(-1, 1), ylim=(-1, 1))
ax.plot([-1, 1], [-1, 1])
ax.axhline(0, color='C1')
ax.axhline(0.5, color='C2')


@image_comparison(
baseline_images=['vlines_basic', 'vlines_with_nan', 'vlines_masked'],
extensions=['png']
Expand Down