Skip to content

Commit f39d35e

Browse files
committed
replace separate function axline_transaxes with new functionality for axline
1 parent 02b8c59 commit f39d35e

File tree

3 files changed

+67
-127
lines changed

3 files changed

+67
-127
lines changed

lib/matplotlib/axes/_axes.py

Lines changed: 16 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,12 @@ def axline(self, xy1, xy2=None, *, slope=None, **kwargs):
808808
all other scales, and thus the behavior is undefined. Please specify
809809
the line using the points *xy1*, *xy2* for non-linear scales.
810810
811+
As part of the ``kwargs``, a 'transform' can be passed. In the case
812+
that a point and a slope are given, the transformation will only be
813+
used for the point and not for the slope, i.e. the slope stays in data
814+
coordinates. This can be used e.g. with ``ax.transAxes`` for drawing grid
815+
lines with a fixed slope.
816+
811817
Parameters
812818
----------
813819
xy1, xy2 : (float, float)
@@ -823,8 +829,7 @@ def axline(self, xy1, xy2=None, *, slope=None, **kwargs):
823829
Other Parameters
824830
----------------
825831
**kwargs
826-
Valid kwargs are `.Line2D` properties, with the exception of
827-
'transform':
832+
Valid kwargs are `.Line2D` properties
828833
829834
%(_Line2D_docstr)s
830835
@@ -839,30 +844,21 @@ def axline(self, xy1, xy2=None, *, slope=None, **kwargs):
839844
840845
>>> axline((0, 0), (1, 1), linewidth=4, color='r')
841846
"""
842-
def _to_points(xy1, xy2, slope):
843-
"""
844-
Check for a valid combination of input parameters and convert
845-
to two points, if necessary.
846-
"""
847-
if (xy2 is None and slope is None or
848-
xy2 is not None and slope is not None):
849-
raise TypeError(
850-
"Exactly one of 'xy2' and 'slope' must be given")
851-
if xy2 is None:
852-
x1, y1 = xy1
853-
xy2 = (x1, y1 + 1) if np.isinf(slope) else (x1 + 1, y1 + slope)
854-
return xy1, xy2
847+
if (xy2 is None and slope is None or
848+
xy2 is not None and slope is not None):
849+
raise TypeError(
850+
"Exactly one of 'xy2' and 'slope' must be given")
855851

856-
if "transform" in kwargs:
857-
raise TypeError("'transform' is not allowed as a kwarg; "
858-
"axline generates its own transform")
859852
if slope is not None and (self.get_xscale() != 'linear' or
860853
self.get_yscale() != 'linear'):
861854
raise TypeError("'slope' cannot be used with non-linear scales")
862855

863856
datalim = [xy1] if xy2 is None else [xy1, xy2]
864-
(x1, y1), (x2, y2) = _to_points(xy1, xy2, slope)
865-
line = mlines._AxLine([x1, x2], [y1, y2], **kwargs)
857+
if "transform" in kwargs:
858+
#TODO: datalim = (kwargs["transform"] + self.transData.inverted()).transform(datalim)?
859+
datalim = []
860+
861+
line = mlines._AxLine(xy1, xy2, slope, **kwargs)
866862
# Like add_line, but correctly handling data limits.
867863
self._set_artist_props(line)
868864
if line.get_clip_path() is None:
@@ -876,59 +872,6 @@ def _to_points(xy1, xy2, slope):
876872
self._request_autoscale_view()
877873
return line
878874

879-
def axline_transaxes(self, xy1, slope, **kwargs):
880-
"""
881-
Add an infinitely long straight line.
882-
883-
The line is defined by a *slope* in data coordinates, set by the
884-
current x and y limits of the axes, and a point in axes-relative
885-
coordinates, which is in the same place on the axes regardless of x
886-
and y limits.
887-
888-
This should only be used with linear scales; the *slope* has no clear
889-
meaning for all other scales, and thus the behavior is undefined.
890-
891-
Parameters
892-
----------
893-
xy1 : (float, float)
894-
Point for the line to pass through, in axes coordinates.
895-
``(0, 0)`` is the bottom left corner of the axes, ``(1, 1)`` is
896-
the upper right.
897-
slope : float
898-
The slope of the line, in data coordinates.
899-
900-
Returns
901-
-------
902-
`.Line2D`
903-
904-
Other Parameters
905-
----------------
906-
**kwargs
907-
Valid kwargs are `.Line2D` properties, with the exception of
908-
'transform':
909-
910-
%(_Line2D_docstr)s
911-
912-
See Also
913-
--------
914-
axhline : for horizontal lines
915-
axvline : for vertical lines
916-
917-
Examples
918-
--------
919-
Draw a thick red line passing through (0, 0)
920-
(the lower left corner of the viewport) with slope 1::
921-
922-
>>> axline_transaxes((0, 0), 1, linewidth=4, color='r')
923-
"""
924-
if "transform" in kwargs:
925-
raise TypeError("'transform' is not allowed as a kwarg; "
926-
"axline_transaxes generates its own transform")
927-
928-
line = mlines._AxLineTransAxes(xy1, slope, **kwargs)
929-
self.add_line(line)
930-
return line
931-
932875
@docstring.dedent_interpd
933876
def axhspan(self, ymin, ymax, xmin=0, xmax=1, **kwargs):
934877
"""

lib/matplotlib/lines.py

Lines changed: 43 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,66 +1406,61 @@ class _AxLine(Line2D):
14061406
transform at draw time.
14071407
"""
14081408

1409+
def __init__(self, xy1, xy2, slope, **kwargs):
1410+
super().__init__([0, 1], [0, 1], **kwargs)
1411+
self._slope = slope
1412+
self._xy1 = xy1
1413+
self._xy2 = xy2
1414+
self._transform = kwargs.get("transform")
1415+
14091416
def get_transform(self):
14101417
ax = self.axes
1411-
(x1, y1), (x2, y2) = ax.transScale.transform([*zip(*self.get_data())])
1412-
dx = x2 - x1
1413-
dy = y2 - y1
1414-
if np.allclose(x1, x2):
1415-
if np.allclose(y1, y2):
1416-
raise ValueError(
1417-
f"Cannot draw a line through two identical points "
1418-
f"(x={self.get_xdata()}, y={self.get_ydata()})")
1419-
# First send y1 to 0 and y2 to 1.
1420-
return (Affine2D.from_values(1, 0, 0, 1 / dy, 0, -y1 / dy)
1421-
+ ax.get_xaxis_transform(which="grid"))
1422-
if np.allclose(y1, y2):
1423-
# First send x1 to 0 and x2 to 1.
1424-
return (Affine2D.from_values(1 / dx, 0, 0, 1, -x1 / dx, 0)
1425-
+ ax.get_yaxis_transform(which="grid"))
1418+
if self._transform is not None:
1419+
points_transform = self._transform + ax.transData.inverted()
1420+
else:
1421+
points_transform = ax.transScale
1422+
1423+
if self._xy2 is not None:
1424+
# two points were given
1425+
(x1, y1), (x2, y2) = points_transform.transform([self._xy1, self._xy2])
1426+
dx = x2 - x1
1427+
dy = y2 - y1
1428+
if np.allclose(x1, x2):
1429+
if np.allclose(y1, y2):
1430+
raise ValueError(
1431+
f"Cannot draw a line through two identical points "
1432+
f"(x={self.get_xdata()}, y={self.get_ydata()})")
1433+
slope = np.inf
1434+
else:
1435+
slope = dy / dx
1436+
else:
1437+
# one point and a slope were given
1438+
x1, y1 = points_transform.transform(self._xy1)
1439+
slope = self._slope
14261440
(vxlo, vylo), (vxhi, vyhi) = ax.transScale.transform(ax.viewLim)
14271441
# General case: find intersections with view limits in either
14281442
# direction, and draw between the middle two points.
1429-
_, start, stop, _ = sorted([
1430-
(vxlo, y1 + (vxlo - x1) * dy / dx),
1431-
(vxhi, y1 + (vxhi - x1) * dy / dx),
1432-
(x1 + (vylo - y1) * dx / dy, vylo),
1433-
(x1 + (vyhi - y1) * dx / dy, vyhi),
1434-
])
1435-
return (BboxTransformFrom(Bbox([*zip(*self.get_data())]))
1436-
+ BboxTransformTo(Bbox([start, stop]))
1443+
if np.isclose(slope, 0):
1444+
start = vxlo, y1
1445+
stop = vxhi, y1
1446+
elif np.isinf(slope):
1447+
start = x1, vylo
1448+
stop = x1, vyhi
1449+
else:
1450+
_, start, stop, _ = sorted([
1451+
(vxlo, y1 + (vxlo - x1) * slope),
1452+
(vxhi, y1 + (vxhi - x1) * slope),
1453+
(x1 + (vylo - y1) / slope, vylo),
1454+
(x1 + (vyhi - y1) / slope, vyhi),
1455+
])
1456+
return (BboxTransformTo(Bbox([start, stop]))
14371457
+ ax.transLimits + ax.transAxes)
14381458

14391459
def draw(self, renderer):
14401460
self._transformed_path = None # Force regen.
14411461
super().draw(renderer)
14421462

14431463

1444-
class _AxLineTransAxes(_AxLine):
1445-
"""
1446-
A helper class that implements `~.Axes.axline_transaxes`, by recomputing
1447-
the artist transform at draw time.
1448-
"""
1449-
1450-
def __init__(self, xy1, slope, **kwargs):
1451-
super().__init__([0, 1], [0, 1], **kwargs)
1452-
self._slope = slope
1453-
self._xy1 = xy1
1454-
1455-
def get_transform(self):
1456-
ax = self.axes
1457-
x, y = self._xy1
1458-
1459-
dx, dy = AffineDeltaTransform(ax.transData + ax.transAxes.inverted())\
1460-
.transform([1, self._slope])
1461-
slope_axes = dy / dx
1462-
1463-
start = 0, slope_axes * (0 - x) + y
1464-
stop = 1, slope_axes * (1 - x) + y
1465-
1466-
return BboxTransformTo(Bbox([start, stop])) + ax.transAxes
1467-
1468-
14691464
class VertexSelector:
14701465
"""
14711466
Manage the callbacks to maintain a list of selected vertices for

lib/matplotlib/tests/test_axes.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4076,15 +4076,17 @@ def test_axline(fig_test, fig_ref):
40764076
def test_axline_transaxes(fig_test, fig_ref):
40774077
ax = fig_test.subplots()
40784078
ax.set(xlim=(-1, 1), ylim=(-1, 1))
4079-
ax.axline_transaxes((0, 0), slope=1)
4080-
ax.axline_transaxes((1, 0.5), slope=1, color='C1')
4081-
ax.axline_transaxes((0.5, 0.5), slope=0, color='C2')
4079+
ax.axline((0, 0), slope=1, transform=ax.transAxes)
4080+
ax.axline((1, 0.5), slope=1, color='C1', transform=ax.transAxes)
4081+
ax.axline((0.5, 0.5), slope=0, color='C2', transform=ax.transAxes)
4082+
ax.axline((0.5, 0), (0.5, 1), color='C3', transform=ax.transAxes)
40824083

40834084
ax = fig_ref.subplots()
40844085
ax.set(xlim=(-1, 1), ylim=(-1, 1))
40854086
ax.plot([-1, 1], [-1, 1])
40864087
ax.plot([0, 1], [-1, 0], color='C1')
40874088
ax.plot([-1, 1], [0, 0], color='C2')
4089+
ax.plot([0, 0], [-1, 1], color='C3')
40884090

40894091

40904092
@check_figures_equal()
@@ -4093,9 +4095,9 @@ def test_axline_transaxes_panzoom(fig_test, fig_ref):
40934095
# figure resize after plotting
40944096
ax = fig_test.subplots()
40954097
ax.set(xlim=(-1, 1), ylim=(-1, 1))
4096-
ax.axline_transaxes((0, 0), slope=1)
4097-
ax.axline_transaxes((0.5, 0.5), slope=2, color='C1')
4098-
ax.axline_transaxes((0.5, 0.5), slope=0, color='C2')
4098+
ax.axline((0, 0), slope=1, transform=ax.transAxes)
4099+
ax.axline((0.5, 0.5), slope=2, color='C1', transform=ax.transAxes)
4100+
ax.axline((0.5, 0.5), slope=0, color='C2', transform=ax.transAxes)
40994101
ax.set(xlim=(0, 5), ylim=(0, 10))
41004102
fig_test.set_size_inches(3, 3)
41014103

0 commit comments

Comments
 (0)