1""" 2Helper classes to adjust the positions of multiple axes at drawing time. 3""" 4 5import numpy as np 6 7from matplotlib import _api 8from matplotlib.axes import SubplotBase 9from matplotlib.gridspec import SubplotSpec, GridSpec 10import matplotlib.transforms as mtransforms 11from . import axes_size as Size 12 13 14class Divider: 15 """ 16 An Axes positioning class. 17 18 The divider is initialized with lists of horizontal and vertical sizes 19 (:mod:`mpl_toolkits.axes_grid1.axes_size`) based on which a given 20 rectangular area will be divided. 21 22 The `new_locator` method then creates a callable object 23 that can be used as the *axes_locator* of the axes. 24 """ 25 26 def __init__(self, fig, pos, horizontal, vertical, 27 aspect=None, anchor="C"): 28 """ 29 Parameters 30 ---------- 31 fig : Figure 32 pos : tuple of 4 floats 33 Position of the rectangle that will be divided. 34 horizontal : list of :mod:`~mpl_toolkits.axes_grid1.axes_size` 35 Sizes for horizontal division. 36 vertical : list of :mod:`~mpl_toolkits.axes_grid1.axes_size` 37 Sizes for vertical division. 38 aspect : bool 39 Whether overall rectangular area is reduced so that the relative 40 part of the horizontal and vertical scales have the same scale. 41 anchor : {'C', 'SW', 'S', 'SE', 'E', 'NE', 'N', 'NW', 'W'} 42 Placement of the reduced rectangle, when *aspect* is True. 43 """ 44 45 self._fig = fig 46 self._pos = pos 47 self._horizontal = horizontal 48 self._vertical = vertical 49 self._anchor = anchor 50 self._aspect = aspect 51 self._xrefindex = 0 52 self._yrefindex = 0 53 self._locator = None 54 55 def get_horizontal_sizes(self, renderer): 56 return [s.get_size(renderer) for s in self.get_horizontal()] 57 58 def get_vertical_sizes(self, renderer): 59 return [s.get_size(renderer) for s in self.get_vertical()] 60 61 def get_vsize_hsize(self): 62 vsize = Size.AddList(self.get_vertical()) 63 hsize = Size.AddList(self.get_horizontal()) 64 return vsize, hsize 65 66 @staticmethod 67 def _calc_k(l, total_size): 68 69 rs_sum, as_sum = 0., 0. 70 71 for _rs, _as in l: 72 rs_sum += _rs 73 as_sum += _as 74 75 if rs_sum != 0.: 76 k = (total_size - as_sum) / rs_sum 77 return k 78 else: 79 return 0. 80 81 @staticmethod 82 def _calc_offsets(l, k): 83 offsets = [0.] 84 for _rs, _as in l: 85 offsets.append(offsets[-1] + _rs*k + _as) 86 return offsets 87 88 def set_position(self, pos): 89 """ 90 Set the position of the rectangle. 91 92 Parameters 93 ---------- 94 pos : tuple of 4 floats 95 position of the rectangle that will be divided 96 """ 97 self._pos = pos 98 99 def get_position(self): 100 """Return the position of the rectangle.""" 101 return self._pos 102 103 def set_anchor(self, anchor): 104 """ 105 Parameters 106 ---------- 107 anchor : {'C', 'SW', 'S', 'SE', 'E', 'NE', 'N', 'NW', 'W'} 108 anchor position 109 110 ===== ============ 111 value description 112 ===== ============ 113 'C' Center 114 'SW' bottom left 115 'S' bottom 116 'SE' bottom right 117 'E' right 118 'NE' top right 119 'N' top 120 'NW' top left 121 'W' left 122 ===== ============ 123 124 """ 125 if len(anchor) != 2: 126 _api.check_in_list(mtransforms.Bbox.coefs, anchor=anchor) 127 self._anchor = anchor 128 129 def get_anchor(self): 130 """Return the anchor.""" 131 return self._anchor 132 133 def set_horizontal(self, h): 134 """ 135 Parameters 136 ---------- 137 h : list of :mod:`~mpl_toolkits.axes_grid1.axes_size` 138 sizes for horizontal division 139 """ 140 self._horizontal = h 141 142 def get_horizontal(self): 143 """Return horizontal sizes.""" 144 return self._horizontal 145 146 def set_vertical(self, v): 147 """ 148 Parameters 149 ---------- 150 v : list of :mod:`~mpl_toolkits.axes_grid1.axes_size` 151 sizes for vertical division 152 """ 153 self._vertical = v 154 155 def get_vertical(self): 156 """Return vertical sizes.""" 157 return self._vertical 158 159 def set_aspect(self, aspect=False): 160 """ 161 Parameters 162 ---------- 163 aspect : bool 164 """ 165 self._aspect = aspect 166 167 def get_aspect(self): 168 """Return aspect.""" 169 return self._aspect 170 171 def set_locator(self, _locator): 172 self._locator = _locator 173 174 def get_locator(self): 175 return self._locator 176 177 def get_position_runtime(self, ax, renderer): 178 if self._locator is None: 179 return self.get_position() 180 else: 181 return self._locator(ax, renderer).bounds 182 183 def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None): 184 """ 185 Parameters 186 ---------- 187 nx, nx1 : int 188 Integers specifying the column-position of the 189 cell. When *nx1* is None, a single *nx*-th column is 190 specified. Otherwise location of columns spanning between *nx* 191 to *nx1* (but excluding *nx1*-th column) is specified. 192 ny, ny1 : int 193 Same as *nx* and *nx1*, but for row positions. 194 axes 195 renderer 196 """ 197 198 figW, figH = self._fig.get_size_inches() 199 x, y, w, h = self.get_position_runtime(axes, renderer) 200 201 hsizes = self.get_horizontal_sizes(renderer) 202 vsizes = self.get_vertical_sizes(renderer) 203 k_h = self._calc_k(hsizes, figW*w) 204 k_v = self._calc_k(vsizes, figH*h) 205 206 if self.get_aspect(): 207 k = min(k_h, k_v) 208 ox = self._calc_offsets(hsizes, k) 209 oy = self._calc_offsets(vsizes, k) 210 211 ww = (ox[-1] - ox[0]) / figW 212 hh = (oy[-1] - oy[0]) / figH 213 pb = mtransforms.Bbox.from_bounds(x, y, w, h) 214 pb1 = mtransforms.Bbox.from_bounds(x, y, ww, hh) 215 pb1_anchored = pb1.anchored(self.get_anchor(), pb) 216 x0, y0 = pb1_anchored.x0, pb1_anchored.y0 217 218 else: 219 ox = self._calc_offsets(hsizes, k_h) 220 oy = self._calc_offsets(vsizes, k_v) 221 x0, y0 = x, y 222 223 if nx1 is None: 224 nx1 = nx + 1 225 if ny1 is None: 226 ny1 = ny + 1 227 228 x1, w1 = x0 + ox[nx] / figW, (ox[nx1] - ox[nx]) / figW 229 y1, h1 = y0 + oy[ny] / figH, (oy[ny1] - oy[ny]) / figH 230 231 return mtransforms.Bbox.from_bounds(x1, y1, w1, h1) 232 233 def new_locator(self, nx, ny, nx1=None, ny1=None): 234 """ 235 Return a new `AxesLocator` for the specified cell. 236 237 Parameters 238 ---------- 239 nx, nx1 : int 240 Integers specifying the column-position of the 241 cell. When *nx1* is None, a single *nx*-th column is 242 specified. Otherwise location of columns spanning between *nx* 243 to *nx1* (but excluding *nx1*-th column) is specified. 244 ny, ny1 : int 245 Same as *nx* and *nx1*, but for row positions. 246 """ 247 return AxesLocator(self, nx, ny, nx1, ny1) 248 249 def append_size(self, position, size): 250 if position == "left": 251 self._horizontal.insert(0, size) 252 self._xrefindex += 1 253 elif position == "right": 254 self._horizontal.append(size) 255 elif position == "bottom": 256 self._vertical.insert(0, size) 257 self._yrefindex += 1 258 elif position == "top": 259 self._vertical.append(size) 260 else: 261 _api.check_in_list(["left", "right", "bottom", "top"], 262 position=position) 263 264 def add_auto_adjustable_area(self, use_axes, pad=0.1, adjust_dirs=None): 265 if adjust_dirs is None: 266 adjust_dirs = ["left", "right", "bottom", "top"] 267 from .axes_size import Padded, SizeFromFunc, GetExtentHelper 268 for d in adjust_dirs: 269 helper = GetExtentHelper(use_axes, d) 270 size = SizeFromFunc(helper) 271 padded_size = Padded(size, pad) # pad in inch 272 self.append_size(d, padded_size) 273 274 275class AxesLocator: 276 """ 277 A simple callable object, initialized with AxesDivider class, 278 returns the position and size of the given cell. 279 """ 280 def __init__(self, axes_divider, nx, ny, nx1=None, ny1=None): 281 """ 282 Parameters 283 ---------- 284 axes_divider : AxesDivider 285 nx, nx1 : int 286 Integers specifying the column-position of the 287 cell. When *nx1* is None, a single *nx*-th column is 288 specified. Otherwise location of columns spanning between *nx* 289 to *nx1* (but excluding *nx1*-th column) is specified. 290 ny, ny1 : int 291 Same as *nx* and *nx1*, but for row positions. 292 """ 293 self._axes_divider = axes_divider 294 295 _xrefindex = axes_divider._xrefindex 296 _yrefindex = axes_divider._yrefindex 297 298 self._nx, self._ny = nx - _xrefindex, ny - _yrefindex 299 300 if nx1 is None: 301 nx1 = nx + 1 302 if ny1 is None: 303 ny1 = ny + 1 304 305 self._nx1 = nx1 - _xrefindex 306 self._ny1 = ny1 - _yrefindex 307 308 def __call__(self, axes, renderer): 309 310 _xrefindex = self._axes_divider._xrefindex 311 _yrefindex = self._axes_divider._yrefindex 312 313 return self._axes_divider.locate(self._nx + _xrefindex, 314 self._ny + _yrefindex, 315 self._nx1 + _xrefindex, 316 self._ny1 + _yrefindex, 317 axes, 318 renderer) 319 320 def get_subplotspec(self): 321 if hasattr(self._axes_divider, "get_subplotspec"): 322 return self._axes_divider.get_subplotspec() 323 else: 324 return None 325 326 327class SubplotDivider(Divider): 328 """ 329 The Divider class whose rectangle area is specified as a subplot geometry. 330 """ 331 332 def __init__(self, fig, *args, horizontal=None, vertical=None, 333 aspect=None, anchor='C'): 334 """ 335 Parameters 336 ---------- 337 fig : `matplotlib.figure.Figure` 338 339 *args : tuple (*nrows*, *ncols*, *index*) or int 340 The array of subplots in the figure has dimensions ``(nrows, 341 ncols)``, and *index* is the index of the subplot being created. 342 *index* starts at 1 in the upper left corner and increases to the 343 right. 344 345 If *nrows*, *ncols*, and *index* are all single digit numbers, then 346 *args* can be passed as a single 3-digit number (e.g. 234 for 347 (2, 3, 4)). 348 """ 349 self.figure = fig 350 super().__init__(fig, [0, 0, 1, 1], 351 horizontal=horizontal or [], vertical=vertical or [], 352 aspect=aspect, anchor=anchor) 353 self.set_subplotspec(SubplotSpec._from_subplot_args(fig, args)) 354 355 def get_position(self): 356 """Return the bounds of the subplot box.""" 357 return self.get_subplotspec().get_position(self.figure).bounds 358 359 @_api.deprecated("3.4") 360 @property 361 def figbox(self): 362 return self.get_subplotspec().get_position(self.figure) 363 364 @_api.deprecated("3.4") 365 def update_params(self): 366 pass 367 368 @_api.deprecated( 369 "3.4", alternative="get_subplotspec", 370 addendum="(get_subplotspec returns a SubplotSpec instance.)") 371 def get_geometry(self): 372 """Get the subplot geometry, e.g., (2, 2, 3).""" 373 rows, cols, num1, num2 = self.get_subplotspec().get_geometry() 374 return rows, cols, num1 + 1 # for compatibility 375 376 @_api.deprecated("3.4", alternative="set_subplotspec") 377 def change_geometry(self, numrows, numcols, num): 378 """Change subplot geometry, e.g., from (1, 1, 1) to (2, 2, 3).""" 379 self._subplotspec = GridSpec(numrows, numcols)[num-1] 380 self.update_params() 381 self.set_position(self.figbox) 382 383 def get_subplotspec(self): 384 """Get the SubplotSpec instance.""" 385 return self._subplotspec 386 387 def set_subplotspec(self, subplotspec): 388 """Set the SubplotSpec instance.""" 389 self._subplotspec = subplotspec 390 self.set_position(subplotspec.get_position(self.figure)) 391 392 393class AxesDivider(Divider): 394 """ 395 Divider based on the pre-existing axes. 396 """ 397 398 def __init__(self, axes, xref=None, yref=None): 399 """ 400 Parameters 401 ---------- 402 axes : :class:`~matplotlib.axes.Axes` 403 xref 404 yref 405 """ 406 self._axes = axes 407 if xref is None: 408 self._xref = Size.AxesX(axes) 409 else: 410 self._xref = xref 411 if yref is None: 412 self._yref = Size.AxesY(axes) 413 else: 414 self._yref = yref 415 416 super().__init__(fig=axes.get_figure(), pos=None, 417 horizontal=[self._xref], vertical=[self._yref], 418 aspect=None, anchor="C") 419 420 def _get_new_axes(self, *, axes_class=None, **kwargs): 421 axes = self._axes 422 if axes_class is None: 423 if isinstance(axes, SubplotBase): 424 axes_class = axes._axes_class 425 else: 426 axes_class = type(axes) 427 return axes_class(axes.get_figure(), axes.get_position(original=True), 428 **kwargs) 429 430 def new_horizontal(self, size, pad=None, pack_start=False, **kwargs): 431 """ 432 Add a new axes on the right (or left) side of the main axes. 433 434 Parameters 435 ---------- 436 size : :mod:`~mpl_toolkits.axes_grid1.axes_size` or float or str 437 A width of the axes. If float or string is given, *from_any* 438 function is used to create the size, with *ref_size* set to AxesX 439 instance of the current axes. 440 pad : :mod:`~mpl_toolkits.axes_grid1.axes_size` or float or str 441 Pad between the axes. It takes same argument as *size*. 442 pack_start : bool 443 If False, the new axes is appended at the end 444 of the list, i.e., it became the right-most axes. If True, it is 445 inserted at the start of the list, and becomes the left-most axes. 446 **kwargs 447 All extra keywords arguments are passed to the created axes. 448 If *axes_class* is given, the new axes will be created as an 449 instance of the given class. Otherwise, the same class of the 450 main axes will be used. 451 """ 452 if pad is None: 453 _api.warn_deprecated( 454 "3.2", message="In a future version, 'pad' will default to " 455 "rcParams['figure.subplot.wspace']. Set pad=0 to keep the " 456 "old behavior.") 457 if pad: 458 if not isinstance(pad, Size._Base): 459 pad = Size.from_any(pad, fraction_ref=self._xref) 460 if pack_start: 461 self._horizontal.insert(0, pad) 462 self._xrefindex += 1 463 else: 464 self._horizontal.append(pad) 465 if not isinstance(size, Size._Base): 466 size = Size.from_any(size, fraction_ref=self._xref) 467 if pack_start: 468 self._horizontal.insert(0, size) 469 self._xrefindex += 1 470 locator = self.new_locator(nx=0, ny=self._yrefindex) 471 else: 472 self._horizontal.append(size) 473 locator = self.new_locator( 474 nx=len(self._horizontal) - 1, ny=self._yrefindex) 475 ax = self._get_new_axes(**kwargs) 476 ax.set_axes_locator(locator) 477 return ax 478 479 def new_vertical(self, size, pad=None, pack_start=False, **kwargs): 480 """ 481 Add a new axes on the top (or bottom) side of the main axes. 482 483 Parameters 484 ---------- 485 size : :mod:`~mpl_toolkits.axes_grid1.axes_size` or float or str 486 A height of the axes. If float or string is given, *from_any* 487 function is used to create the size, with *ref_size* set to AxesX 488 instance of the current axes. 489 pad : :mod:`~mpl_toolkits.axes_grid1.axes_size` or float or str 490 Pad between the axes. It takes same argument as *size*. 491 pack_start : bool 492 If False, the new axes is appended at the end 493 of the list, i.e., it became the right-most axes. If True, it is 494 inserted at the start of the list, and becomes the left-most axes. 495 **kwargs 496 All extra keywords arguments are passed to the created axes. 497 If *axes_class* is given, the new axes will be created as an 498 instance of the given class. Otherwise, the same class of the 499 main axes will be used. 500 """ 501 if pad is None: 502 _api.warn_deprecated( 503 "3.2", message="In a future version, 'pad' will default to " 504 "rcParams['figure.subplot.hspace']. Set pad=0 to keep the " 505 "old behavior.") 506 if pad: 507 if not isinstance(pad, Size._Base): 508 pad = Size.from_any(pad, fraction_ref=self._yref) 509 if pack_start: 510 self._vertical.insert(0, pad) 511 self._yrefindex += 1 512 else: 513 self._vertical.append(pad) 514 if not isinstance(size, Size._Base): 515 size = Size.from_any(size, fraction_ref=self._yref) 516 if pack_start: 517 self._vertical.insert(0, size) 518 self._yrefindex += 1 519 locator = self.new_locator(nx=self._xrefindex, ny=0) 520 else: 521 self._vertical.append(size) 522 locator = self.new_locator( 523 nx=self._xrefindex, ny=len(self._vertical)-1) 524 ax = self._get_new_axes(**kwargs) 525 ax.set_axes_locator(locator) 526 return ax 527 528 def append_axes(self, position, size, pad=None, add_to_figure=True, 529 **kwargs): 530 """ 531 Create an axes at the given *position* with the same height 532 (or width) of the main axes. 533 534 *position* 535 ["left"|"right"|"bottom"|"top"] 536 537 *size* and *pad* should be axes_grid.axes_size compatible. 538 """ 539 if position == "left": 540 ax = self.new_horizontal(size, pad, pack_start=True, **kwargs) 541 elif position == "right": 542 ax = self.new_horizontal(size, pad, pack_start=False, **kwargs) 543 elif position == "bottom": 544 ax = self.new_vertical(size, pad, pack_start=True, **kwargs) 545 elif position == "top": 546 ax = self.new_vertical(size, pad, pack_start=False, **kwargs) 547 else: 548 _api.check_in_list(["left", "right", "bottom", "top"], 549 position=position) 550 if add_to_figure: 551 self._fig.add_axes(ax) 552 return ax 553 554 def get_aspect(self): 555 if self._aspect is None: 556 aspect = self._axes.get_aspect() 557 if aspect == "auto": 558 return False 559 else: 560 return True 561 else: 562 return self._aspect 563 564 def get_position(self): 565 if self._pos is None: 566 bbox = self._axes.get_position(original=True) 567 return bbox.bounds 568 else: 569 return self._pos 570 571 def get_anchor(self): 572 if self._anchor is None: 573 return self._axes.get_anchor() 574 else: 575 return self._anchor 576 577 def get_subplotspec(self): 578 if hasattr(self._axes, "get_subplotspec"): 579 return self._axes.get_subplotspec() 580 else: 581 return None 582 583 584class HBoxDivider(SubplotDivider): 585 586 @staticmethod 587 def _determine_karray(equivalent_sizes, appended_sizes, 588 max_equivalent_size, 589 total_appended_size): 590 591 n = len(equivalent_sizes) 592 eq_rs, eq_as = np.asarray(equivalent_sizes).T 593 ap_rs, ap_as = np.asarray(appended_sizes).T 594 A = np.zeros((n + 1, n + 1)) 595 B = np.zeros(n + 1) 596 np.fill_diagonal(A[:n, :n], eq_rs) 597 A[:n, -1] = -1 598 A[-1, :-1] = ap_rs 599 B[:n] = -eq_as 600 B[-1] = total_appended_size - sum(ap_as) 601 602 karray_H = np.linalg.solve(A, B) # A @ K = B 603 karray = karray_H[:-1] 604 H = karray_H[-1] 605 606 if H > max_equivalent_size: 607 karray = (max_equivalent_size - eq_as) / eq_rs 608 return karray 609 610 @staticmethod 611 def _calc_offsets(appended_sizes, karray): 612 offsets = [0.] 613 for (r, a), k in zip(appended_sizes, karray): 614 offsets.append(offsets[-1] + r*k + a) 615 return offsets 616 617 def new_locator(self, nx, nx1=None): 618 """ 619 Create a new `AxesLocator` for the specified cell. 620 621 Parameters 622 ---------- 623 nx, nx1 : int 624 Integers specifying the column-position of the 625 cell. When *nx1* is None, a single *nx*-th column is 626 specified. Otherwise location of columns spanning between *nx* 627 to *nx1* (but excluding *nx1*-th column) is specified. 628 ny, ny1 : int 629 Same as *nx* and *nx1*, but for row positions. 630 """ 631 return AxesLocator(self, nx, 0, nx1, None) 632 633 def _locate(self, x, y, w, h, 634 y_equivalent_sizes, x_appended_sizes, 635 figW, figH): 636 equivalent_sizes = y_equivalent_sizes 637 appended_sizes = x_appended_sizes 638 639 max_equivalent_size = figH * h 640 total_appended_size = figW * w 641 karray = self._determine_karray(equivalent_sizes, appended_sizes, 642 max_equivalent_size, 643 total_appended_size) 644 645 ox = self._calc_offsets(appended_sizes, karray) 646 647 ww = (ox[-1] - ox[0]) / figW 648 ref_h = equivalent_sizes[0] 649 hh = (karray[0]*ref_h[0] + ref_h[1]) / figH 650 pb = mtransforms.Bbox.from_bounds(x, y, w, h) 651 pb1 = mtransforms.Bbox.from_bounds(x, y, ww, hh) 652 pb1_anchored = pb1.anchored(self.get_anchor(), pb) 653 x0, y0 = pb1_anchored.x0, pb1_anchored.y0 654 655 return x0, y0, ox, hh 656 657 def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None): 658 """ 659 Parameters 660 ---------- 661 axes_divider : AxesDivider 662 nx, nx1 : int 663 Integers specifying the column-position of the 664 cell. When *nx1* is None, a single *nx*-th column is 665 specified. Otherwise location of columns spanning between *nx* 666 to *nx1* (but excluding *nx1*-th column) is specified. 667 ny, ny1 : int 668 Same as *nx* and *nx1*, but for row positions. 669 axes 670 renderer 671 """ 672 673 figW, figH = self._fig.get_size_inches() 674 x, y, w, h = self.get_position_runtime(axes, renderer) 675 676 y_equivalent_sizes = self.get_vertical_sizes(renderer) 677 x_appended_sizes = self.get_horizontal_sizes(renderer) 678 x0, y0, ox, hh = self._locate(x, y, w, h, 679 y_equivalent_sizes, x_appended_sizes, 680 figW, figH) 681 if nx1 is None: 682 nx1 = nx + 1 683 684 x1, w1 = x0 + ox[nx] / figW, (ox[nx1] - ox[nx]) / figW 685 y1, h1 = y0, hh 686 687 return mtransforms.Bbox.from_bounds(x1, y1, w1, h1) 688 689 690class VBoxDivider(HBoxDivider): 691 """ 692 The Divider class whose rectangle area is specified as a subplot geometry. 693 """ 694 695 def new_locator(self, ny, ny1=None): 696 """ 697 Create a new `AxesLocator` for the specified cell. 698 699 Parameters 700 ---------- 701 ny, ny1 : int 702 Integers specifying the row-position of the 703 cell. When *ny1* is None, a single *ny*-th row is 704 specified. Otherwise location of rows spanning between *ny* 705 to *ny1* (but excluding *ny1*-th row) is specified. 706 """ 707 return AxesLocator(self, 0, ny, None, ny1) 708 709 def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None): 710 """ 711 Parameters 712 ---------- 713 axes_divider : AxesDivider 714 nx, nx1 : int 715 Integers specifying the column-position of the 716 cell. When *nx1* is None, a single *nx*-th column is 717 specified. Otherwise location of columns spanning between *nx* 718 to *nx1* (but excluding *nx1*-th column) is specified. 719 ny, ny1 : int 720 Same as *nx* and *nx1*, but for row positions. 721 axes 722 renderer 723 """ 724 725 figW, figH = self._fig.get_size_inches() 726 x, y, w, h = self.get_position_runtime(axes, renderer) 727 728 x_equivalent_sizes = self.get_horizontal_sizes(renderer) 729 y_appended_sizes = self.get_vertical_sizes(renderer) 730 731 y0, x0, oy, ww = self._locate(y, x, h, w, 732 x_equivalent_sizes, y_appended_sizes, 733 figH, figW) 734 if ny1 is None: 735 ny1 = ny + 1 736 737 x1, w1 = x0, ww 738 y1, h1 = y0 + oy[ny] / figH, (oy[ny1] - oy[ny]) / figH 739 740 return mtransforms.Bbox.from_bounds(x1, y1, w1, h1) 741 742 743def make_axes_locatable(axes): 744 divider = AxesDivider(axes) 745 locator = divider.new_locator(nx=0, ny=0) 746 axes.set_axes_locator(locator) 747 748 return divider 749 750 751def make_axes_area_auto_adjustable(ax, 752 use_axes=None, pad=0.1, 753 adjust_dirs=None): 754 if adjust_dirs is None: 755 adjust_dirs = ["left", "right", "bottom", "top"] 756 divider = make_axes_locatable(ax) 757 758 if use_axes is None: 759 use_axes = ax 760 761 divider.add_auto_adjustable_area(use_axes=use_axes, pad=pad, 762 adjust_dirs=adjust_dirs) 763