Skip to content
Merged
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: 4 additions & 1 deletion docs/1dplots.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,8 @@
# Also, the default *x* bounds for lines drawn with :func:`~ultraplot.axes.PlotAxes.plot`
# and *y* bounds for lines drawn with :func:`~ultraplot.axes.PlotAxes.plotx` are now
# "sticky", i.e. there is no padding between the lines and axes edges by default.
# This can be disabled globally with :rcraw:`axes.sticky_edges` or per-axes with
# the ``use_sticky_edges`` attribute.
#
# Step and stem plots can be drawn with :func:`~ultraplot.axes.PlotAxes.step`,
# :func:`~ultraplot.axes.PlotAxes.stepx`, :func:`~ultraplot.axes.PlotAxes.stem`, and
Expand Down Expand Up @@ -505,7 +507,8 @@
# ``stacked=True``. Also note the default *x* bounds for shading drawn with
# :func:`~ultraplot.axes.PlotAxes.area` and *y* bounds for shading drawn with
# :func:`~ultraplot.axes.PlotAxes.areax` is now "sticky", i.e. there is no padding
# between the shading and axes edges by default.
# between the shading and axes edges by default. This can be disabled globally with
# :rcraw:`axes.sticky_edges` or per-axes with the ``use_sticky_edges`` attribute.

# .. important::
#
Expand Down
15 changes: 15 additions & 0 deletions ultraplot/axes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,7 @@ def __init__(self, *args, **kwargs):
self._title_loc = None
self._title_pad = rc["title.pad"]
self._title_pad_current = None
self._use_sticky_edges = rc["axes.sticky_edges"]
self._altx_parent = None # for cartesian axes only
self._alty_parent = None
self._colorbar_fill = None
Expand Down Expand Up @@ -4701,6 +4702,20 @@ def number(self, num):
else:
raise ValueError(f"Invalid number {num!r}. Must be integer >=1.")

@property
def use_sticky_edges(self):
"""
Whether plotting commands like `plot`, `plotx`, `vlines`, `hlines`,
`fill_between`, and `fill_betweenx` add "sticky" edges to their artists,
i.e. whether the default axis limits are the artist bounds with no padding.
Initialized from :rcraw:`axes.sticky_edges`.
"""
return self._use_sticky_edges

@use_sticky_edges.setter
def use_sticky_edges(self, value):
self._use_sticky_edges = rcsetup._validate_bool(value)


# Apply signature obfuscation after storing previous signature
# NOTE: This is needed for __init__
Expand Down
19 changes: 11 additions & 8 deletions ultraplot/axes/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2115,14 +2115,15 @@ def curved_quiver(
arrows.append(p)

lc = mcollections.LineCollection(streamlines, transform=transform, **line_kw)
lc.sticky_edges.x[:] = [
solver.grid.x_origin,
solver.grid.x_origin + solver.grid.width,
]
lc.sticky_edges.y[:] = [
solver.grid.y_origin,
solver.grid.y_origin + solver.grid.height,
]
if self.use_sticky_edges:
lc.sticky_edges.x[:] = [
solver.grid.x_origin,
solver.grid.x_origin + solver.grid.width,
]
lc.sticky_edges.y[:] = [
solver.grid.y_origin,
solver.grid.y_origin + solver.grid.height,
]

if use_multicolor_lines:
lc.set_array(np.ma.hstack(line_colors))
Expand Down Expand Up @@ -3506,6 +3507,8 @@ def _fix_sticky_edges(self, objs, axis, *args, only=None):
Fix sticky edges for the input artists using the minimum and maximum of the
input coordinates. This is used to copy `bar` behavior to `area` and `lines`.
"""
if not self.use_sticky_edges:
return
for array in args:
min_, max_ = inputs._safe_range(array)
if min_ is None or max_ is None:
Expand Down
8 changes: 8 additions & 0 deletions ultraplot/internals/rcsetup.py
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,14 @@ def _validator_accepts(validator, value):
_validate_float,
"The fractional *x* and *y* axis margins when limits are unset.",
),
"axes.sticky_edges": (
True,
_validate_bool,
"Whether artists added by plotting commands like `plot`, `plotx`, "
"`vlines`, `hlines`, `fill_between`, and `fill_betweenx` are given "
'"sticky" edges, i.e. whether the default axis limits are the artist '
"bounds with no padding. See also `Axes.use_sticky_edges`.",
),
"bar.bar_labels": (
False,
_validate_bool,
Expand Down
55 changes: 55 additions & 0 deletions ultraplot/tests/test_1dplots.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,61 @@ def test_bar_absolute_width_manual_override():
assert pytest.approx(w_abs[0], rel=1e-6) == 0.8


def test_sticky_edges_rc_and_attribute():
"""
Sticky edges for line and area plots can be disabled globally with the
``axes.sticky_edges`` setting or per-axes with ``use_sticky_edges``.
See https://github.com/Ultraplot/UltraPlot/issues/631.
"""
x = y = np.arange(10.0)

# Default: sticky edges enabled, no padding
fig, axs = uplt.subplots()
ax = axs[0]
assert ax.use_sticky_edges is True
ax.plot(x, y)
assert np.allclose(ax.get_xlim(), (0, 9))

# Disabled globally via rc setting
with uplt.rc.context({"axes.sticky_edges": False}):
fig, axs = uplt.subplots()
ax = axs[0]
assert ax.use_sticky_edges is False
ax.plot(x, y)
xmin, xmax = ax.get_xlim()
assert xmin < 0 and xmax > 9

# plotx, area, and vlines also respect the setting
fig, axs = uplt.subplots(nrows=3)
axs[0].plotx(y, x)
ymin, ymax = axs[0].get_ylim()
assert ymin < 0 and ymax > 9
axs[1].area(x, y)
xmin, xmax = axs[1].get_xlim()
assert xmin < 0 and xmax > 9
axs[2].vlines(x, 0, y)
ymin, ymax = axs[2].get_ylim()
assert ymin < 0 and ymax > 9

# Setting is restored after the context exits
fig, axs = uplt.subplots()
assert axs[0].use_sticky_edges is True

# Disabled per-axes via attribute
fig, axs = uplt.subplots()
ax = axs[0]
ax.use_sticky_edges = False
ax.plot(x, y)
xmin, xmax = ax.get_xlim()
assert xmin < 0 and xmax > 9

# Attribute values are validated as booleans
ax.use_sticky_edges = "true"
assert ax.use_sticky_edges is True
with pytest.raises(ValueError):
ax.use_sticky_edges = "bogus"


import pytest


Expand Down