diff --git a/doc/users/next_whats_new/2017-12-08-axline.rst b/doc/users/next_whats_new/2017-12-08-axline.rst new file mode 100644 index 000000000000..22e64a04b8cc --- /dev/null +++ b/doc/users/next_whats_new/2017-12-08-axline.rst @@ -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. diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index ebef65a6fd68..1d7a6560271d 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -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 -------- @@ -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: @@ -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): """ diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 25dc08dc5420..39abbe198714 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -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']