Skip to content

Commit b98dbdc

Browse files
ENH: Allow tupple for borderpad in AnchoredOffsetbox
1 parent 53af231 commit b98dbdc

File tree

6 files changed

+84
-10
lines changed

6 files changed

+84
-10
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
``borderpad`` accepts a tuple for separate x/y padding
2+
-------------------------------------------------------
3+
4+
The ``borderpad`` parameter used for placing anchored artists (such as inset axes) now accepts a tuple of ``(x_pad, y_pad)``.
5+
6+
This allows for specifying separate padding values for the horizontal and
7+
vertical directions, providing finer control over placement. For example, when
8+
placing an inset in a corner, one might want horizontal padding to avoid
9+
overlapping with the main plot's axis labels, but no vertical padding to keep
10+
the inset flush with the plot area edge.
11+
12+
Example usage with :func:`~mpl_toolkits.axes_grid1.inset_locator.inset_axes`:
13+
14+
.. code-block:: python
15+
16+
ax_inset = inset_axes(
17+
ax, width="30%", height="30%", loc='upper left',
18+
borderpad=(4, 0))

lib/matplotlib/legend.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1140,9 +1140,10 @@ def _get_anchored_bbox(self, loc, bbox, parentbbox, renderer):
11401140
parentbbox : `~matplotlib.transforms.Bbox`
11411141
A parent box which will contain the bbox, in display coordinates.
11421142
"""
1143+
pad = self.borderaxespad * renderer.points_to_pixels(self._fontsize)
11431144
return offsetbox._get_anchored_bbox(
11441145
loc, bbox, parentbbox,
1145-
self.borderaxespad * renderer.points_to_pixels(self._fontsize))
1146+
pad,pad)
11461147

11471148
def _find_best_position(self, width, height, renderer):
11481149
"""Determine the best location to place the legend."""

lib/matplotlib/offsetbox.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -946,8 +946,10 @@ def __init__(self, loc, *,
946946
See the parameter *loc* of `.Legend` for details.
947947
pad : float, default: 0.4
948948
Padding around the child as fraction of the fontsize.
949-
borderpad : float, default: 0.5
949+
borderpad : float or (float, float), default: 0.5
950950
Padding between the offsetbox frame and the *bbox_to_anchor*.
951+
If a float, the same padding is used for both x and y.
952+
If a tuple of two floats, it specifies the (x, y) padding.
951953
child : `.OffsetBox`
952954
The box that will be anchored.
953955
prop : `.FontProperties`
@@ -1054,12 +1056,22 @@ def set_bbox_to_anchor(self, bbox, transform=None):
10541056
@_compat_get_offset
10551057
def get_offset(self, bbox, renderer):
10561058
# docstring inherited
1057-
pad = (self.borderpad
1058-
* renderer.points_to_pixels(self.prop.get_size_in_points()))
1059+
fontsize_in_pixels = renderer.points_to_pixels(self.prop.get_size_in_points())
1060+
try:
1061+
borderpad_x, borderpad_y = self.borderpad
1062+
except TypeError:
1063+
borderpad_x = self.borderpad
1064+
borderpad_y = self.borderpad
1065+
pad_x_pixels = borderpad_x * fontsize_in_pixels
1066+
pad_y_pixels = borderpad_y * fontsize_in_pixels
10591067
bbox_to_anchor = self.get_bbox_to_anchor()
10601068
x0, y0 = _get_anchored_bbox(
1061-
self.loc, Bbox.from_bounds(0, 0, bbox.width, bbox.height),
1062-
bbox_to_anchor, pad)
1069+
self.loc,
1070+
Bbox.from_bounds(0, 0, bbox.width, bbox.height),
1071+
bbox_to_anchor,
1072+
pad_x_pixels,
1073+
pad_y_pixels
1074+
)
10631075
return x0 - bbox.x0, y0 - bbox.y0
10641076

10651077
def update_frame(self, bbox, fontsize=None):
@@ -1084,15 +1096,16 @@ def draw(self, renderer):
10841096
self.stale = False
10851097

10861098

1087-
def _get_anchored_bbox(loc, bbox, parentbbox, borderpad):
1099+
def _get_anchored_bbox(loc, bbox, parentbbox, pad_x, pad_y):
10881100
"""
10891101
Return the (x, y) position of the *bbox* anchored at the *parentbbox* with
10901102
the *loc* code with the *borderpad*.
1103+
The paddings are specified by *pad_x* and *pad_y*.
10911104
"""
10921105
# This is only called internally and *loc* should already have been
10931106
# validated. If 0 (None), we just let ``bbox.anchored`` raise.
10941107
c = [None, "NE", "NW", "SW", "SE", "E", "W", "E", "S", "N", "C"][loc]
1095-
container = parentbbox.padded(-borderpad)
1108+
container = parentbbox.padded(-pad_x, -pad_y)
10961109
return bbox.anchored(c, container=container).p0
10971110

10981111

lib/matplotlib/offsetbox.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ class AnchoredOffsetbox(OffsetBox):
157157
loc: str,
158158
*,
159159
pad: float = ...,
160-
borderpad: float = ...,
160+
borderpad: float | tuple[float, float] = ...,
161161
child: OffsetBox | None = ...,
162162
prop: FontProperties | None = ...,
163163
frameon: bool = ...,

lib/matplotlib/tests/test_offsetbox.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,3 +470,39 @@ def test_draggable_in_subfigure():
470470
bbox = ann.get_window_extent()
471471
MouseEvent("button_press_event", fig.canvas, bbox.x1+2, bbox.y1+2)._process()
472472
assert not ann._draggable.got_artist
473+
474+
475+
def test_anchored_offsetbox_tuple_and_float_borderpad():
476+
"""
477+
Test AnchoredOffsetbox correctly handles both float and tuple for borderpad.
478+
"""
479+
480+
fig, ax = plt.subplots()
481+
482+
# Case 1: Establish a baseline with float value
483+
text_float = AnchoredText("float", loc='lower left', borderpad=5)
484+
ax.add_artist(text_float)
485+
486+
# Case 2: Test that a symmetric tuple gives the exact same result.
487+
text_tuple_equal = AnchoredText("tuple", loc='lower left', borderpad=(5, 5))
488+
ax.add_artist(text_tuple_equal)
489+
490+
# Case 3: Test that an asymmetric tuple with larger values works as expected.
491+
text_tuple_asym = AnchoredText("tuple_asym", loc='lower left', borderpad=(10, 4))
492+
ax.add_artist(text_tuple_asym)
493+
494+
# Draw the canvas to calculate final positions
495+
fig.canvas.draw()
496+
497+
pos_float = text_float.get_window_extent()
498+
pos_tuple_equal = text_tuple_equal.get_window_extent()
499+
pos_tuple_asym = text_tuple_asym.get_window_extent()
500+
501+
# Assertion 1: Prove that borderpad=5 is identical to borderpad=(5, 5).
502+
assert pos_tuple_equal.x0 == pos_float.x0
503+
assert pos_tuple_equal.y0 == pos_float.y0
504+
505+
# Assertion 2: Prove that the larger, asymmetric padding moved the box
506+
# further from the origin than the baseline.
507+
assert pos_tuple_asym.x0 > pos_float.x0
508+
assert pos_tuple_asym.y0 < pos_float.y0

lib/mpl_toolkits/axes_grid1/inset_locator.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ def inset_axes(parent_axes, width, height, loc='upper right',
256256
axes_class=None, axes_kwargs=None,
257257
borderpad=0.5):
258258
"""
259+
259260
Create an inset axes with a given width and height.
260261
261262
Both sizes used can be specified either in inches or percentage.
@@ -341,11 +342,16 @@ def inset_axes(parent_axes, width, height, loc='upper right',
341342
342343
%(Axes:kwdoc)s
343344
344-
borderpad : float, default: 0.5
345+
borderpad : float or (float, float), default: 0.5
345346
Padding between inset axes and the bbox_to_anchor.
347+
If a float, the same padding is used for both x and y.
348+
If a tuple of two floats, it specifies the (x, y) padding.
346349
The units are axes font size, i.e. for a default font size of 10 points
347350
*borderpad = 0.5* is equivalent to a padding of 5 points.
348351
352+
.. versionadded:: 3.10
353+
The *borderpad* parameter now accepts a tuple of (x, y) paddings.
354+
349355
Returns
350356
-------
351357
inset_axes : *axes_class*

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy