Skip to content

Commit bb75f73

Browse files
authoredApr 2, 2020
Merge pull request matplotlib#16337 from timhoffm/axline-slope
Create axline() using slope
2 parents 97d9cd1 + 42e5fc0 commit bb75f73

File tree

3 files changed

+58
-8
lines changed

3 files changed

+58
-8
lines changed
 

‎lib/matplotlib/axes/_axes.py

+32-6
Original file line numberDiff line numberDiff line change
@@ -905,18 +905,27 @@ def axvline(self, x=0, ymin=0, ymax=1, **kwargs):
905905
return l
906906

907907
@docstring.dedent_interpd
908-
def axline(self, xy1, xy2, **kwargs):
908+
def axline(self, xy1, xy2=None, *, slope=None, **kwargs):
909909
"""
910-
Add an infinitely long straight line that passes through two points.
910+
Add an infinitely long straight line.
911+
912+
The line can be defined either by two points *xy1* and *xy2*, or
913+
by one point *xy1* and a *slope*.
911914
912915
This draws a straight line "on the screen", regardless of the x and y
913916
scales, and is thus also suitable for drawing exponential decays in
914-
semilog plots, power laws in loglog plots, etc.
917+
semilog plots, power laws in loglog plots, etc. However, *slope*
918+
should only be used with linear scales; It has no clear meaning for
919+
all other scales, and thus the behavior is undefined. Please specify
920+
the line using the points *xy1*, *xy2* for non-linear scales.
915921
916922
Parameters
917923
----------
918924
xy1, xy2 : (float, float)
919925
Points for the line to pass through.
926+
Either *xy2* or *slope* has to be given.
927+
slope : float, optional
928+
The slope of the line. Either *xy2* or *slope* has to be given.
920929
921930
Returns
922931
-------
@@ -941,12 +950,29 @@ def axline(self, xy1, xy2, **kwargs):
941950
942951
>>> axline((0, 0), (1, 1), linewidth=4, color='r')
943952
"""
953+
def _to_points(xy1, xy2, slope):
954+
"""
955+
Check for a valid combination of input parameters and convert
956+
to two points, if necessary.
957+
"""
958+
if (xy2 is None and slope is None or
959+
xy2 is not None and slope is not None):
960+
raise TypeError(
961+
"Exactly one of 'xy2' and 'slope' must be given")
962+
if xy2 is None:
963+
x1, y1 = xy1
964+
xy2 = (x1, y1 + 1) if np.isinf(slope) else (x1 + 1, y1 + slope)
965+
return xy1, xy2
944966

945967
if "transform" in kwargs:
946968
raise TypeError("'transform' is not allowed as a kwarg; "
947969
"axline generates its own transform")
948-
x1, y1 = xy1
949-
x2, y2 = xy2
970+
if slope is not None and (self.get_xscale() != 'linear' or
971+
self.get_yscale() != 'linear'):
972+
raise TypeError("'slope' cannot be used with non-linear scales")
973+
974+
datalim = [xy1] if xy2 is None else [xy1, xy2]
975+
(x1, y1), (x2, y2) = _to_points(xy1, xy2, slope)
950976
line = mlines._AxLine([x1, x2], [y1, y2], **kwargs)
951977
# Like add_line, but correctly handling data limits.
952978
self._set_artist_props(line)
@@ -956,7 +982,7 @@ def axline(self, xy1, xy2, **kwargs):
956982
line.set_label(f"_line{len(self.lines)}")
957983
self.lines.append(line)
958984
line._remove_method = self.lines.remove
959-
self.update_datalim([xy1, xy2])
985+
self.update_datalim(datalim)
960986

961987
self._request_autoscale_view()
962988
return line

‎lib/matplotlib/pyplot.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -2290,8 +2290,8 @@ def axis(*args, emit=True, **kwargs):
22902290

22912291
# Autogenerated by boilerplate.py. Do not edit as changes will be lost.
22922292
@_copy_docstring_and_deprecators(Axes.axline)
2293-
def axline(xy1, xy2, **kwargs):
2294-
return gca().axline(xy1, xy2, **kwargs)
2293+
def axline(xy1, xy2=None, *, slope=None, **kwargs):
2294+
return gca().axline(xy1, xy2=xy2, slope=slope, **kwargs)
22952295

22962296

22972297
# Autogenerated by boilerplate.py. Do not edit as changes will be lost.

‎lib/matplotlib/tests/test_axes.py

+24
Original file line numberDiff line numberDiff line change
@@ -3931,12 +3931,36 @@ def test_axline(fig_test, fig_ref):
39313931
ax.axline((0, 0), (1, 1))
39323932
ax.axline((0, 0), (1, 0), color='C1')
39333933
ax.axline((0, 0.5), (1, 0.5), color='C2')
3934+
# slopes
3935+
ax.axline((-0.7, -0.5), slope=0, color='C3')
3936+
ax.axline((1, -0.5), slope=-0.5, color='C4')
3937+
ax.axline((-0.5, 1), slope=float('inf'), color='C5')
39343938

39353939
ax = fig_ref.subplots()
39363940
ax.set(xlim=(-1, 1), ylim=(-1, 1))
39373941
ax.plot([-1, 1], [-1, 1])
39383942
ax.axhline(0, color='C1')
39393943
ax.axhline(0.5, color='C2')
3944+
# slopes
3945+
ax.axhline(-0.5, color='C3')
3946+
ax.plot([-1, 1], [0.5, -0.5], color='C4')
3947+
ax.axvline(-0.5, color='C5')
3948+
3949+
3950+
def test_axline_args():
3951+
"""Exactly one of *xy2* and *slope* must be specified."""
3952+
fig, ax = plt.subplots()
3953+
with pytest.raises(TypeError):
3954+
ax.axline((0, 0)) # missing second parameter
3955+
with pytest.raises(TypeError):
3956+
ax.axline((0, 0), (1, 1), slope=1) # redundant parameters
3957+
ax.set_xscale('log')
3958+
with pytest.raises(TypeError):
3959+
ax.axline((0, 0), slope=1)
3960+
ax.set_xscale('linear')
3961+
ax.set_yscale('log')
3962+
with pytest.raises(TypeError):
3963+
ax.axline((0, 0), slope=1)
39403964

39413965

39423966
@image_comparison(['vlines_basic', 'vlines_with_nan', 'vlines_masked'],

0 commit comments

Comments
 (0)