diff --git a/doc/users/next_whats_new/updated_borderpad_parameter.rst b/doc/users/next_whats_new/updated_borderpad_parameter.rst new file mode 100644 index 000000000000..5acf075f7b51 --- /dev/null +++ b/doc/users/next_whats_new/updated_borderpad_parameter.rst @@ -0,0 +1,18 @@ +``borderpad`` accepts a tuple for separate x/y padding +------------------------------------------------------- + +The ``borderpad`` parameter used for placing anchored artists (such as inset axes) now accepts a tuple of ``(x_pad, y_pad)``. + +This allows for specifying separate padding values for the horizontal and +vertical directions, providing finer control over placement. For example, when +placing an inset in a corner, one might want horizontal padding to avoid +overlapping with the main plot's axis labels, but no vertical padding to keep +the inset flush with the plot area edge. + +Example usage with :func:`~mpl_toolkits.axes_grid1.inset_locator.inset_axes`: + +.. code-block:: python + + ax_inset = inset_axes( + ax, width="30%", height="30%", loc='upper left', + borderpad=(4, 0)) diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index 2fb14e52c58c..933b3f7c9eaa 100644 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -1140,9 +1140,10 @@ def _get_anchored_bbox(self, loc, bbox, parentbbox, renderer): parentbbox : `~matplotlib.transforms.Bbox` A parent box which will contain the bbox, in display coordinates. """ + pad = self.borderaxespad * renderer.points_to_pixels(self._fontsize) return offsetbox._get_anchored_bbox( loc, bbox, parentbbox, - self.borderaxespad * renderer.points_to_pixels(self._fontsize)) + pad, pad) def _find_best_position(self, width, height, renderer): """Determine the best location to place the legend.""" diff --git a/lib/matplotlib/offsetbox.py b/lib/matplotlib/offsetbox.py index 974cc4f2db05..39035e0b785a 100644 --- a/lib/matplotlib/offsetbox.py +++ b/lib/matplotlib/offsetbox.py @@ -946,8 +946,13 @@ def __init__(self, loc, *, See the parameter *loc* of `.Legend` for details. pad : float, default: 0.4 Padding around the child as fraction of the fontsize. - borderpad : float, default: 0.5 + borderpad : float or (float, float), default: 0.5 Padding between the offsetbox frame and the *bbox_to_anchor*. + If a float, the same padding is used for both x and y. + If a tuple of two floats, it specifies the (x, y) padding. + + .. versionadded:: 3.11 + The *borderpad* parameter now accepts a tuple of (x, y) paddings. child : `.OffsetBox` The box that will be anchored. prop : `.FontProperties` @@ -1054,12 +1059,22 @@ def set_bbox_to_anchor(self, bbox, transform=None): @_compat_get_offset def get_offset(self, bbox, renderer): # docstring inherited - pad = (self.borderpad - * renderer.points_to_pixels(self.prop.get_size_in_points())) + fontsize_in_pixels = renderer.points_to_pixels(self.prop.get_size_in_points()) + try: + borderpad_x, borderpad_y = self.borderpad + except TypeError: + borderpad_x = self.borderpad + borderpad_y = self.borderpad + pad_x_pixels = borderpad_x * fontsize_in_pixels + pad_y_pixels = borderpad_y * fontsize_in_pixels bbox_to_anchor = self.get_bbox_to_anchor() x0, y0 = _get_anchored_bbox( - self.loc, Bbox.from_bounds(0, 0, bbox.width, bbox.height), - bbox_to_anchor, pad) + self.loc, + Bbox.from_bounds(0, 0, bbox.width, bbox.height), + bbox_to_anchor, + pad_x_pixels, + pad_y_pixels + ) return x0 - bbox.x0, y0 - bbox.y0 def update_frame(self, bbox, fontsize=None): @@ -1084,15 +1099,15 @@ def draw(self, renderer): self.stale = False -def _get_anchored_bbox(loc, bbox, parentbbox, borderpad): +def _get_anchored_bbox(loc, bbox, parentbbox, pad_x, pad_y): """ Return the (x, y) position of the *bbox* anchored at the *parentbbox* with - the *loc* code with the *borderpad*. + the *loc* code with the *borderpad* and padding *pad_x*, *pad_y*. """ # This is only called internally and *loc* should already have been # validated. If 0 (None), we just let ``bbox.anchored`` raise. c = [None, "NE", "NW", "SW", "SE", "E", "W", "E", "S", "N", "C"][loc] - container = parentbbox.padded(-borderpad) + container = parentbbox.padded(-pad_x, -pad_y) return bbox.anchored(c, container=container).p0 diff --git a/lib/matplotlib/offsetbox.pyi b/lib/matplotlib/offsetbox.pyi index 8a2016c0320a..36f31908eebf 100644 --- a/lib/matplotlib/offsetbox.pyi +++ b/lib/matplotlib/offsetbox.pyi @@ -157,7 +157,7 @@ class AnchoredOffsetbox(OffsetBox): loc: str, *, pad: float = ..., - borderpad: float = ..., + borderpad: float | tuple[float, float] = ..., child: OffsetBox | None = ..., prop: FontProperties | None = ..., frameon: bool = ..., @@ -185,7 +185,7 @@ class AnchoredText(AnchoredOffsetbox): loc: str, *, pad: float = ..., - borderpad: float = ..., + borderpad: float | tuple[float, float] = ..., prop: dict[str, Any] | None = ..., **kwargs ) -> None: ... diff --git a/lib/matplotlib/tests/test_offsetbox.py b/lib/matplotlib/tests/test_offsetbox.py index bd353ffc719b..f126b1cbb466 100644 --- a/lib/matplotlib/tests/test_offsetbox.py +++ b/lib/matplotlib/tests/test_offsetbox.py @@ -470,3 +470,40 @@ def test_draggable_in_subfigure(): bbox = ann.get_window_extent() MouseEvent("button_press_event", fig.canvas, bbox.x1+2, bbox.y1+2)._process() assert not ann._draggable.got_artist + + +def test_anchored_offsetbox_tuple_and_float_borderpad(): + """ + Test AnchoredOffsetbox correctly handles both float and tuple for borderpad. + """ + + fig, ax = plt.subplots() + + # Case 1: Establish a baseline with float value + text_float = AnchoredText("float", loc='lower left', borderpad=5) + ax.add_artist(text_float) + + # Case 2: Test that a symmetric tuple gives the exact same result. + text_tuple_equal = AnchoredText("tuple", loc='lower left', borderpad=(5, 5)) + ax.add_artist(text_tuple_equal) + + # Case 3: Test that an asymmetric tuple with different values works as expected. + text_tuple_asym = AnchoredText("tuple_asym", loc='lower left', borderpad=(10, 4)) + ax.add_artist(text_tuple_asym) + + # Draw the canvas to calculate final positions + fig.canvas.draw() + + pos_float = text_float.get_window_extent() + pos_tuple_equal = text_tuple_equal.get_window_extent() + pos_tuple_asym = text_tuple_asym.get_window_extent() + + # Assertion 1: Prove that borderpad=5 is identical to borderpad=(5, 5). + assert pos_tuple_equal.x0 == pos_float.x0 + assert pos_tuple_equal.y0 == pos_float.y0 + + # Assertion 2: Prove that the asymmetric padding moved the box + # further from the origin than the baseline in the x-direction and less far + # in the y-direction. + assert pos_tuple_asym.x0 > pos_float.x0 + assert pos_tuple_asym.y0 < pos_float.y0 diff --git a/lib/mpl_toolkits/axes_grid1/inset_locator.py b/lib/mpl_toolkits/axes_grid1/inset_locator.py index 52fe6efc0618..a1a9cc8df591 100644 --- a/lib/mpl_toolkits/axes_grid1/inset_locator.py +++ b/lib/mpl_toolkits/axes_grid1/inset_locator.py @@ -341,11 +341,16 @@ def inset_axes(parent_axes, width, height, loc='upper right', %(Axes:kwdoc)s - borderpad : float, default: 0.5 + borderpad : float or (float, float), default: 0.5 Padding between inset axes and the bbox_to_anchor. + If a float, the same padding is used for both x and y. + If a tuple of two floats, it specifies the (x, y) padding. The units are axes font size, i.e. for a default font size of 10 points *borderpad = 0.5* is equivalent to a padding of 5 points. + .. versionadded:: 3.11 + The *borderpad* parameter now accepts a tuple of (x, y) paddings. + Returns ------- inset_axes : *axes_class* 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