1"""
2The axes_divider module provides helper classes to adjust the positions of
3multiple axes at drawing time.
4
5 Divider: this is the class that is used to calculate the axes
6    position. It divides the given rectangular area into several sub
7    rectangles. You initialize the divider by setting the horizontal
8    and vertical lists of sizes that the division will be based on. You
9    then use the new_locator method, whose return value is a callable
10    object that can be used to set the axes_locator of the axes.
11
12"""
13from __future__ import (absolute_import, division, print_function,
14                        unicode_literals)
15
16import six
17from six.moves import map
18
19import matplotlib.transforms as mtransforms
20
21from matplotlib.axes import SubplotBase
22
23from . import axes_size as Size
24
25
26class Divider(object):
27    """
28    This class calculates the axes position. It
29    divides the given rectangular area into several
30    sub-rectangles. You initialize the divider by setting the
31    horizontal and vertical lists of sizes
32    (:mod:`mpl_toolkits.axes_grid.axes_size`) that the division will
33    be based on. You then use the new_locator method to create a
34    callable object that can be used as the axes_locator of the
35    axes.
36    """
37
38    def __init__(self, fig, pos, horizontal, vertical,
39                 aspect=None, anchor="C"):
40        """
41        Parameters
42        ----------
43        fig : Figure
44        pos : tuple of 4 floats
45            position of the rectangle that will be divided
46        horizontal : list of :mod:`~mpl_toolkits.axes_grid.axes_size`
47            sizes for horizontal division
48        vertical : list of :mod:`~mpl_toolkits.axes_grid.axes_size`
49            sizes for vertical division
50        aspect : bool
51            if True, the overall rectangular area is reduced
52            so that the relative part of the horizontal and
53            vertical scales have the same scale.
54        anchor : {'C', 'SW', 'S', 'SE', 'E', 'NE', 'N', 'NW', 'W'}
55            placement of the reduced rectangle when *aspect* is True
56        """
57
58        self._fig = fig
59        self._pos = pos
60        self._horizontal = horizontal
61        self._vertical = vertical
62        self._anchor = anchor
63        self._aspect = aspect
64        self._xrefindex = 0
65        self._yrefindex = 0
66        self._locator = None
67
68    def get_horizontal_sizes(self, renderer):
69        return [s.get_size(renderer) for s in self.get_horizontal()]
70
71    def get_vertical_sizes(self, renderer):
72        return [s.get_size(renderer) for s in self.get_vertical()]
73
74    def get_vsize_hsize(self):
75
76        from .axes_size import AddList
77
78        vsize = AddList(self.get_vertical())
79        hsize = AddList(self.get_horizontal())
80
81        return vsize, hsize
82
83    @staticmethod
84    def _calc_k(l, total_size):
85
86        rs_sum, as_sum = 0., 0.
87
88        for _rs, _as in l:
89            rs_sum += _rs
90            as_sum += _as
91
92        if rs_sum != 0.:
93            k = (total_size - as_sum) / rs_sum
94            return k
95        else:
96            return 0.
97
98    @staticmethod
99    def _calc_offsets(l, k):
100
101        offsets = [0.]
102
103        #for s in l:
104        for _rs, _as in l:
105            #_rs, _as = s.get_size(renderer)
106            offsets.append(offsets[-1] + _rs*k + _as)
107
108        return offsets
109
110    def set_position(self, pos):
111        """
112        set the position of the rectangle.
113
114        Parameters
115        ----------
116        pos : tuple of 4 floats
117            position of the rectangle that will be divided
118        """
119        self._pos = pos
120
121    def get_position(self):
122        "return the position of the rectangle."
123        return self._pos
124
125    def set_anchor(self, anchor):
126        """
127        Parameters
128        ----------
129        anchor : {'C', 'SW', 'S', 'SE', 'E', 'NE', 'N', 'NW', 'W'}
130            anchor position
131
132          =====  ============
133          value  description
134          =====  ============
135          'C'    Center
136          'SW'   bottom left
137          'S'    bottom
138          'SE'   bottom right
139          'E'    right
140          'NE'   top right
141          'N'    top
142          'NW'   top left
143          'W'    left
144          =====  ============
145
146        """
147        if anchor in mtransforms.Bbox.coefs or len(anchor) == 2:
148            self._anchor = anchor
149        else:
150            raise ValueError('argument must be among %s' %
151                             ', '.join(mtransforms.BBox.coefs))
152
153    def get_anchor(self):
154        "return the anchor"
155        return self._anchor
156
157    def set_horizontal(self, h):
158        """
159        Parameters
160        ----------
161        h : list of :mod:`~mpl_toolkits.axes_grid.axes_size`
162            sizes for horizontal division
163        """
164        self._horizontal = h
165
166    def get_horizontal(self):
167        "return horizontal sizes"
168        return self._horizontal
169
170    def set_vertical(self, v):
171        """
172        Parameters
173        ----------
174        v : list of :mod:`~mpl_toolkits.axes_grid.axes_size`
175            sizes for vertical division
176        """
177        self._vertical = v
178
179    def get_vertical(self):
180        "return vertical sizes"
181        return self._vertical
182
183    def set_aspect(self, aspect=False):
184        """
185        Parameters
186        ----------
187        aspect : bool
188        """
189        self._aspect = aspect
190
191    def get_aspect(self):
192        "return aspect"
193        return self._aspect
194
195    def set_locator(self, _locator):
196        self._locator = _locator
197
198    def get_locator(self):
199        return self._locator
200
201    def get_position_runtime(self, ax, renderer):
202        if self._locator is None:
203            return self.get_position()
204        else:
205            return self._locator(ax, renderer).bounds
206
207    def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None):
208        """
209        Parameters
210        ----------
211        nx, nx1 : int
212            Integers specifying the column-position of the
213            cell. When *nx1* is None, a single *nx*-th column is
214            specified. Otherwise location of columns spanning between *nx*
215            to *nx1* (but excluding *nx1*-th column) is specified.
216        ny, ny1 : int
217            Same as *nx* and *nx1*, but for row positions.
218        axes
219        renderer
220        """
221
222        figW, figH = self._fig.get_size_inches()
223        x, y, w, h = self.get_position_runtime(axes, renderer)
224
225        hsizes = self.get_horizontal_sizes(renderer)
226        vsizes = self.get_vertical_sizes(renderer)
227        k_h = self._calc_k(hsizes, figW*w)
228        k_v = self._calc_k(vsizes, figH*h)
229
230        if self.get_aspect():
231            k = min(k_h, k_v)
232            ox = self._calc_offsets(hsizes, k)
233            oy = self._calc_offsets(vsizes, k)
234
235            ww = (ox[-1] - ox[0])/figW
236            hh = (oy[-1] - oy[0])/figH
237            pb = mtransforms.Bbox.from_bounds(x, y, w, h)
238            pb1 = mtransforms.Bbox.from_bounds(x, y, ww, hh)
239            pb1_anchored = pb1.anchored(self.get_anchor(), pb)
240            x0, y0 = pb1_anchored.x0, pb1_anchored.y0
241
242        else:
243            ox = self._calc_offsets(hsizes, k_h)
244            oy = self._calc_offsets(vsizes, k_v)
245            x0, y0 = x, y
246
247        if nx1 is None:
248            nx1 = nx+1
249        if ny1 is None:
250            ny1 = ny+1
251
252        x1, w1 = x0 + ox[nx]/figW, (ox[nx1] - ox[nx])/figW
253        y1, h1 = y0 + oy[ny]/figH, (oy[ny1] - oy[ny])/figH
254
255        return mtransforms.Bbox.from_bounds(x1, y1, w1, h1)
256
257    def new_locator(self, nx, ny, nx1=None, ny1=None):
258        """
259        Returns a new locator
260        (:class:`mpl_toolkits.axes_grid.axes_divider.AxesLocator`) for
261        specified cell.
262
263        Parameters
264        ----------
265        nx, nx1 : int
266            Integers specifying the column-position of the
267            cell. When *nx1* is None, a single *nx*-th column is
268            specified. Otherwise location of columns spanning between *nx*
269            to *nx1* (but excluding *nx1*-th column) is specified.
270        ny, ny1 : int
271            Same as *nx* and *nx1*, but for row positions.
272        """
273        return AxesLocator(self, nx, ny, nx1, ny1)
274
275    def append_size(self, position, size):
276
277        if position == "left":
278            self._horizontal.insert(0, size)
279            self._xrefindex += 1
280        elif position == "right":
281            self._horizontal.append(size)
282        elif position == "bottom":
283            self._vertical.insert(0, size)
284            self._yrefindex += 1
285        elif position == "top":
286            self._vertical.append(size)
287        else:
288            raise ValueError("the position must be one of left," +
289                             " right, bottom, or top")
290
291    def add_auto_adjustable_area(self,
292                                 use_axes, pad=0.1,
293                                 adjust_dirs=None,
294                                 ):
295        if adjust_dirs is None:
296            adjust_dirs = ["left", "right", "bottom", "top"]
297        from .axes_size import Padded, SizeFromFunc, GetExtentHelper
298        for d in adjust_dirs:
299            helper = GetExtentHelper(use_axes, d)
300            size = SizeFromFunc(helper)
301            padded_size = Padded(size, pad)  # pad in inch
302            self.append_size(d, padded_size)
303
304
305class AxesLocator(object):
306    """
307    A simple callable object, initialized with AxesDivider class,
308    returns the position and size of the given cell.
309    """
310    def __init__(self, axes_divider, nx, ny, nx1=None, ny1=None):
311        """
312        Parameters
313        ----------
314        axes_divider : AxesDivider
315        nx, nx1 : int
316            Integers specifying the column-position of the
317            cell. When *nx1* is None, a single *nx*-th column is
318            specified. Otherwise location of columns spanning between *nx*
319            to *nx1* (but excluding *nx1*-th column) is specified.
320        ny, ny1 : int
321            Same as *nx* and *nx1*, but for row positions.
322        """
323        self._axes_divider = axes_divider
324
325        _xrefindex = axes_divider._xrefindex
326        _yrefindex = axes_divider._yrefindex
327
328        self._nx, self._ny = nx - _xrefindex, ny - _yrefindex
329
330        if nx1 is None:
331            nx1 = nx+1
332        if ny1 is None:
333            ny1 = ny+1
334
335        self._nx1 = nx1 - _xrefindex
336        self._ny1 = ny1 - _yrefindex
337
338    def __call__(self, axes, renderer):
339
340        _xrefindex = self._axes_divider._xrefindex
341        _yrefindex = self._axes_divider._yrefindex
342
343        return self._axes_divider.locate(self._nx + _xrefindex,
344                                         self._ny + _yrefindex,
345                                         self._nx1 + _xrefindex,
346                                         self._ny1 + _yrefindex,
347                                         axes,
348                                         renderer)
349
350    def get_subplotspec(self):
351        if hasattr(self._axes_divider, "get_subplotspec"):
352            return self._axes_divider.get_subplotspec()
353        else:
354            return None
355
356
357from matplotlib.gridspec import SubplotSpec, GridSpec
358
359
360class SubplotDivider(Divider):
361    """
362    The Divider class whose rectangle area is specified as a subplot geometry.
363    """
364
365    def __init__(self, fig, *args, **kwargs):
366        """
367        Parameters
368        ----------
369        fig : :class:`matplotlib.figure.Figure`
370        args : tuple (*numRows*, *numCols*, *plotNum*)
371            The array of subplots in the figure has dimensions *numRows*,
372            *numCols*, and *plotNum* is the number of the subplot
373            being created.  *plotNum* starts at 1 in the upper left
374            corner and increases to the right.
375
376            If *numRows* <= *numCols* <= *plotNum* < 10, *args* can be the
377            decimal integer *numRows* * 100 + *numCols* * 10 + *plotNum*.
378        """
379
380        self.figure = fig
381
382        if len(args) == 1:
383            if isinstance(args[0], SubplotSpec):
384                self._subplotspec = args[0]
385            else:
386                try:
387                    s = str(int(args[0]))
388                    rows, cols, num = map(int, s)
389                except ValueError:
390                    raise ValueError(
391                        'Single argument to subplot must be a 3-digit integer')
392                self._subplotspec = GridSpec(rows, cols)[num-1]
393                # num - 1 for converting from MATLAB to python indexing
394        elif len(args) == 3:
395            rows, cols, num = args
396            rows = int(rows)
397            cols = int(cols)
398            if isinstance(num, tuple) and len(num) == 2:
399                num = [int(n) for n in num]
400                self._subplotspec = GridSpec(rows, cols)[num[0]-1:num[1]]
401            else:
402                self._subplotspec = GridSpec(rows, cols)[int(num)-1]
403                # num - 1 for converting from MATLAB to python indexing
404        else:
405            raise ValueError('Illegal argument(s) to subplot: %s' % (args,))
406
407        # total = rows*cols
408        # num -= 1    # convert from matlab to python indexing
409        #             # i.e., num in range(0,total)
410        # if num >= total:
411        #     raise ValueError( 'Subplot number exceeds total subplots')
412        # self._rows = rows
413        # self._cols = cols
414        # self._num = num
415
416        # self.update_params()
417
418        # sets self.fixbox
419        self.update_params()
420
421        pos = self.figbox.bounds
422
423        horizontal = kwargs.pop("horizontal", [])
424        vertical = kwargs.pop("vertical", [])
425        aspect = kwargs.pop("aspect", None)
426        anchor = kwargs.pop("anchor", "C")
427
428        if kwargs:
429            raise Exception("")
430
431        Divider.__init__(self, fig, pos, horizontal, vertical,
432                         aspect=aspect, anchor=anchor)
433
434    def get_position(self):
435        "return the bounds of the subplot box"
436
437        self.update_params()  # update self.figbox
438        return self.figbox.bounds
439
440    # def update_params(self):
441    #     'update the subplot position from fig.subplotpars'
442
443    #     rows = self._rows
444    #     cols = self._cols
445    #     num = self._num
446
447    #     pars = self.figure.subplotpars
448    #     left = pars.left
449    #     right = pars.right
450    #     bottom = pars.bottom
451    #     top = pars.top
452    #     wspace = pars.wspace
453    #     hspace = pars.hspace
454    #     totWidth = right-left
455    #     totHeight = top-bottom
456
457    #     figH = totHeight/(rows + hspace*(rows-1))
458    #     sepH = hspace*figH
459
460    #     figW = totWidth/(cols + wspace*(cols-1))
461    #     sepW = wspace*figW
462
463    #     rowNum, colNum =  divmod(num, cols)
464
465    #     figBottom = top - (rowNum+1)*figH - rowNum*sepH
466    #     figLeft = left + colNum*(figW + sepW)
467
468    #     self.figbox = mtransforms.Bbox.from_bounds(figLeft, figBottom,
469    #                                                figW, figH)
470
471    def update_params(self):
472        'update the subplot position from fig.subplotpars'
473
474        self.figbox = self.get_subplotspec().get_position(self.figure)
475
476    def get_geometry(self):
477        'get the subplot geometry, e.g., 2,2,3'
478        rows, cols, num1, num2 = self.get_subplotspec().get_geometry()
479        return rows, cols, num1+1  # for compatibility
480
481    # COVERAGE NOTE: Never used internally or from examples
482    def change_geometry(self, numrows, numcols, num):
483        'change subplot geometry, e.g., from 1,1,1 to 2,2,3'
484        self._subplotspec = GridSpec(numrows, numcols)[num-1]
485        self.update_params()
486        self.set_position(self.figbox)
487
488    def get_subplotspec(self):
489        'get the SubplotSpec instance'
490        return self._subplotspec
491
492    def set_subplotspec(self, subplotspec):
493        'set the SubplotSpec instance'
494        self._subplotspec = subplotspec
495
496
497class AxesDivider(Divider):
498    """
499    Divider based on the pre-existing axes.
500    """
501
502    def __init__(self, axes, xref=None, yref=None):
503        """
504        Parameters
505        ----------
506        axes : :class:`~matplotlib.axes.Axes`
507        xref
508        yref
509        """
510        self._axes = axes
511        if xref is None:
512            self._xref = Size.AxesX(axes)
513        else:
514            self._xref = xref
515        if yref is None:
516            self._yref = Size.AxesY(axes)
517        else:
518            self._yref = yref
519
520        Divider.__init__(self, fig=axes.get_figure(), pos=None,
521                         horizontal=[self._xref], vertical=[self._yref],
522                         aspect=None, anchor="C")
523
524    def _get_new_axes(self, **kwargs):
525        axes = self._axes
526
527        axes_class = kwargs.pop("axes_class", None)
528
529        if axes_class is None:
530            if isinstance(axes, SubplotBase):
531                axes_class = axes._axes_class
532            else:
533                axes_class = type(axes)
534
535        ax = axes_class(axes.get_figure(),
536                        axes.get_position(original=True), **kwargs)
537
538        return ax
539
540    def new_horizontal(self, size, pad=None, pack_start=False, **kwargs):
541        """
542        Add a new axes on the right (or left) side of the main axes.
543
544        Parameters
545        ----------
546        size : :mod:`~mpl_toolkits.axes_grid.axes_size` or float or string
547            A width of the axes. If float or string is given, *from_any*
548            function is used to create the size, with *ref_size* set to AxesX
549            instance of the current axes.
550        pad : :mod:`~mpl_toolkits.axes_grid.axes_size` or float or string
551            Pad between the axes. It takes same argument as *size*.
552        pack_start : bool
553            If False, the new axes is appended at the end
554            of the list, i.e., it became the right-most axes. If True, it is
555            inserted at the start of the list, and becomes the left-most axes.
556        kwargs
557            All extra keywords arguments are passed to the created axes.
558            If *axes_class* is given, the new axes will be created as an
559            instance of the given class. Otherwise, the same class of the
560            main axes will be used.
561        """
562
563        if pad:
564            if not isinstance(pad, Size._Base):
565                pad = Size.from_any(pad,
566                                    fraction_ref=self._xref)
567            if pack_start:
568                self._horizontal.insert(0, pad)
569                self._xrefindex += 1
570            else:
571                self._horizontal.append(pad)
572
573        if not isinstance(size, Size._Base):
574            size = Size.from_any(size,
575                                 fraction_ref=self._xref)
576
577        if pack_start:
578            self._horizontal.insert(0, size)
579            self._xrefindex += 1
580            locator = self.new_locator(nx=0, ny=self._yrefindex)
581        else:
582            self._horizontal.append(size)
583            locator = self.new_locator(nx=len(self._horizontal)-1, ny=self._yrefindex)
584
585        ax = self._get_new_axes(**kwargs)
586        ax.set_axes_locator(locator)
587
588        return ax
589
590    def new_vertical(self, size, pad=None, pack_start=False, **kwargs):
591        """
592        Add a new axes on the top (or bottom) side of the main axes.
593
594        Parameters
595        ----------
596        size : :mod:`~mpl_toolkits.axes_grid.axes_size` or float or string
597            A height of the axes. If float or string is given, *from_any*
598            function is used to create the size, with *ref_size* set to AxesX
599            instance of the current axes.
600        pad : :mod:`~mpl_toolkits.axes_grid.axes_size` or float or string
601            Pad between the axes. It takes same argument as *size*.
602        pack_start : bool
603            If False, the new axes is appended at the end
604            of the list, i.e., it became the right-most axes. If True, it is
605            inserted at the start of the list, and becomes the left-most axes.
606        kwargs
607            All extra keywords arguments are passed to the created axes.
608            If *axes_class* is given, the new axes will be created as an
609            instance of the given class. Otherwise, the same class of the
610            main axes will be used.
611        """
612
613        if pad:
614            if not isinstance(pad, Size._Base):
615                pad = Size.from_any(pad,
616                                    fraction_ref=self._yref)
617            if pack_start:
618                self._vertical.insert(0, pad)
619                self._yrefindex += 1
620            else:
621                self._vertical.append(pad)
622
623        if not isinstance(size, Size._Base):
624            size = Size.from_any(size,
625                                 fraction_ref=self._yref)
626
627        if pack_start:
628            self._vertical.insert(0, size)
629            self._yrefindex += 1
630            locator = self.new_locator(nx=self._xrefindex, ny=0)
631        else:
632            self._vertical.append(size)
633            locator = self.new_locator(nx=self._xrefindex, ny=len(self._vertical)-1)
634
635        ax = self._get_new_axes(**kwargs)
636        ax.set_axes_locator(locator)
637
638        return ax
639
640    def append_axes(self, position, size, pad=None, add_to_figure=True,
641                    **kwargs):
642        """
643        create an axes at the given *position* with the same height
644        (or width) of the main axes.
645
646         *position*
647           ["left"|"right"|"bottom"|"top"]
648
649         *size* and *pad* should be axes_grid.axes_size compatible.
650        """
651
652        if position == "left":
653            ax = self.new_horizontal(size, pad, pack_start=True, **kwargs)
654        elif position == "right":
655            ax = self.new_horizontal(size, pad, pack_start=False, **kwargs)
656        elif position == "bottom":
657            ax = self.new_vertical(size, pad, pack_start=True, **kwargs)
658        elif position == "top":
659            ax = self.new_vertical(size, pad, pack_start=False, **kwargs)
660        else:
661            raise ValueError("the position must be one of left," +
662                             " right, bottom, or top")
663
664        if add_to_figure:
665            self._fig.add_axes(ax)
666        return ax
667
668    def get_aspect(self):
669        if self._aspect is None:
670            aspect = self._axes.get_aspect()
671            if aspect == "auto":
672                return False
673            else:
674                return True
675        else:
676            return self._aspect
677
678    def get_position(self):
679        if self._pos is None:
680            bbox = self._axes.get_position(original=True)
681            return bbox.bounds
682        else:
683            return self._pos
684
685    def get_anchor(self):
686        if self._anchor is None:
687            return self._axes.get_anchor()
688        else:
689            return self._anchor
690
691    def get_subplotspec(self):
692        if hasattr(self._axes, "get_subplotspec"):
693            return self._axes.get_subplotspec()
694        else:
695            return None
696
697
698class HBoxDivider(SubplotDivider):
699
700    def __init__(self, fig, *args, **kwargs):
701        SubplotDivider.__init__(self, fig, *args, **kwargs)
702
703    @staticmethod
704    def _determine_karray(equivalent_sizes, appended_sizes,
705                          max_equivalent_size,
706                          total_appended_size):
707
708        n = len(equivalent_sizes)
709        import numpy as np
710        A = np.mat(np.zeros((n+1, n+1), dtype="d"))
711        B = np.zeros((n+1), dtype="d")
712        # AxK = B
713
714        # populated A
715        for i, (r, a) in enumerate(equivalent_sizes):
716            A[i, i] = r
717            A[i, -1] = -1
718            B[i] = -a
719        A[-1, :-1] = [r for r, a in appended_sizes]
720        B[-1] = total_appended_size - sum([a for rs, a in appended_sizes])
721
722        karray_H = (A.I*np.mat(B).T).A1
723        karray = karray_H[:-1]
724        H = karray_H[-1]
725
726        if H > max_equivalent_size:
727            karray = ((max_equivalent_size -
728                      np.array([a for r, a in equivalent_sizes]))
729                      / np.array([r for r, a in equivalent_sizes]))
730        return karray
731
732    @staticmethod
733    def _calc_offsets(appended_sizes, karray):
734        offsets = [0.]
735
736        #for s in l:
737        for (r, a), k in zip(appended_sizes, karray):
738            offsets.append(offsets[-1] + r*k + a)
739
740        return offsets
741
742    def new_locator(self, nx, nx1=None):
743        """
744        returns a new locator
745        (:class:`mpl_toolkits.axes_grid.axes_divider.AxesLocator`) for
746        specified cell.
747
748        Parameters
749        ----------
750        nx, nx1 : int
751            Integers specifying the column-position of the
752            cell. When *nx1* is None, a single *nx*-th column is
753            specified. Otherwise location of columns spanning between *nx*
754            to *nx1* (but excluding *nx1*-th column) is specified.
755        ny, ny1 : int
756            Same as *nx* and *nx1*, but for row positions.
757        """
758        return AxesLocator(self, nx, 0, nx1, None)
759
760    def _locate(self, x, y, w, h,
761                y_equivalent_sizes, x_appended_sizes,
762                figW, figH):
763        """
764        Parameters
765        ----------
766        x
767        y
768        w
769        h
770        y_equivalent_sizes
771        x_appended_sizes
772        figW
773        figH
774        """
775
776        equivalent_sizes = y_equivalent_sizes
777        appended_sizes = x_appended_sizes
778
779        max_equivalent_size = figH*h
780        total_appended_size = figW*w
781        karray = self._determine_karray(equivalent_sizes, appended_sizes,
782                                        max_equivalent_size,
783                                        total_appended_size)
784
785        ox = self._calc_offsets(appended_sizes, karray)
786
787        ww = (ox[-1] - ox[0])/figW
788        ref_h = equivalent_sizes[0]
789        hh = (karray[0]*ref_h[0] + ref_h[1])/figH
790        pb = mtransforms.Bbox.from_bounds(x, y, w, h)
791        pb1 = mtransforms.Bbox.from_bounds(x, y, ww, hh)
792        pb1_anchored = pb1.anchored(self.get_anchor(), pb)
793        x0, y0 = pb1_anchored.x0, pb1_anchored.y0
794
795        return x0, y0, ox, hh
796
797    def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None):
798        """
799        Parameters
800        ----------
801        axes_divider : AxesDivider
802        nx, nx1 : int
803            Integers specifying the column-position of the
804            cell. When *nx1* is None, a single *nx*-th column is
805            specified. Otherwise location of columns spanning between *nx*
806            to *nx1* (but excluding *nx1*-th column) is specified.
807        ny, ny1 : int
808            Same as *nx* and *nx1*, but for row positions.
809        axes
810        renderer
811        """
812
813        figW, figH = self._fig.get_size_inches()
814        x, y, w, h = self.get_position_runtime(axes, renderer)
815
816        y_equivalent_sizes = self.get_vertical_sizes(renderer)
817        x_appended_sizes = self.get_horizontal_sizes(renderer)
818        x0, y0, ox, hh = self._locate(x, y, w, h,
819                                      y_equivalent_sizes, x_appended_sizes,
820                                      figW, figH)
821        if nx1 is None:
822            nx1 = nx+1
823
824        x1, w1 = x0 + ox[nx]/figW, (ox[nx1] - ox[nx])/figW
825        y1, h1 = y0, hh
826
827        return mtransforms.Bbox.from_bounds(x1, y1, w1, h1)
828
829
830class VBoxDivider(HBoxDivider):
831    """
832    The Divider class whose rectangle area is specified as a subplot geometry.
833    """
834
835    def new_locator(self, ny, ny1=None):
836        """
837        returns a new locator
838        (:class:`mpl_toolkits.axes_grid.axes_divider.AxesLocator`) for
839        specified cell.
840
841        Parameters
842        ----------
843        ny, ny1 : int
844            Integers specifying the row-position of the
845            cell. When *ny1* is None, a single *ny*-th row is
846            specified. Otherwise location of rows spanning between *ny*
847            to *ny1* (but excluding *ny1*-th row) is specified.
848        """
849        return AxesLocator(self, 0, ny, None, ny1)
850
851    def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None):
852        """
853        Parameters
854        ----------
855        axes_divider : AxesDivider
856        nx, nx1 : int
857            Integers specifying the column-position of the
858            cell. When *nx1* is None, a single *nx*-th column is
859            specified. Otherwise location of columns spanning between *nx*
860            to *nx1* (but excluding *nx1*-th column) is specified.
861        ny, ny1 : int
862            Same as *nx* and *nx1*, but for row positions.
863        axes
864        renderer
865        """
866
867        figW, figH = self._fig.get_size_inches()
868        x, y, w, h = self.get_position_runtime(axes, renderer)
869
870        x_equivalent_sizes = self.get_horizontal_sizes(renderer)
871        y_appended_sizes = self.get_vertical_sizes(renderer)
872
873        y0, x0, oy, ww = self._locate(y, x, h, w,
874                                      x_equivalent_sizes, y_appended_sizes,
875                                      figH, figW)
876        if ny1 is None:
877            ny1 = ny+1
878
879        x1, w1 = x0, ww
880        y1, h1 = y0 + oy[ny]/figH, (oy[ny1] - oy[ny])/figH
881
882        return mtransforms.Bbox.from_bounds(x1, y1, w1, h1)
883
884
885class LocatableAxesBase(object):
886    def __init__(self, *kl, **kw):
887
888        self._axes_class.__init__(self, *kl, **kw)
889
890        self._locator = None
891        self._locator_renderer = None
892
893    def set_axes_locator(self, locator):
894        self._locator = locator
895
896    def get_axes_locator(self):
897        return self._locator
898
899    def apply_aspect(self, position=None):
900
901        if self.get_axes_locator() is None:
902            self._axes_class.apply_aspect(self, position)
903        else:
904            pos = self.get_axes_locator()(self, self._locator_renderer)
905            self._axes_class.apply_aspect(self, position=pos)
906
907    def draw(self, renderer=None, inframe=False):
908
909        self._locator_renderer = renderer
910
911        self._axes_class.draw(self, renderer, inframe)
912
913    def _make_twin_axes(self, *kl, **kwargs):
914        """
915        Need to overload so that twinx/twiny will work with
916        these axes.
917        """
918        if 'sharex' in kwargs and 'sharey' in kwargs:
919            raise ValueError("Twinned Axes may share only one axis.")
920        ax2 = type(self)(self.figure, self.get_position(True), *kl, **kwargs)
921        ax2.set_axes_locator(self.get_axes_locator())
922        self.figure.add_axes(ax2)
923        self.set_adjustable('datalim')
924        ax2.set_adjustable('datalim')
925        self._twinned_axes.join(self, ax2)
926        return ax2
927
928_locatableaxes_classes = {}
929
930
931def locatable_axes_factory(axes_class):
932
933    new_class = _locatableaxes_classes.get(axes_class)
934    if new_class is None:
935        new_class = type(str("Locatable%s" % (axes_class.__name__)),
936                         (LocatableAxesBase, axes_class),
937                         {'_axes_class': axes_class})
938
939        _locatableaxes_classes[axes_class] = new_class
940
941    return new_class
942
943#if hasattr(maxes.Axes, "get_axes_locator"):
944#    LocatableAxes = maxes.Axes
945#else:
946
947
948def make_axes_locatable(axes):
949    if not hasattr(axes, "set_axes_locator"):
950        new_class = locatable_axes_factory(type(axes))
951        axes.__class__ = new_class
952
953    divider = AxesDivider(axes)
954    locator = divider.new_locator(nx=0, ny=0)
955    axes.set_axes_locator(locator)
956
957    return divider
958
959
960def make_axes_area_auto_adjustable(ax,
961                                   use_axes=None, pad=0.1,
962                                   adjust_dirs=None):
963    if adjust_dirs is None:
964        adjust_dirs = ["left", "right", "bottom", "top"]
965    divider = make_axes_locatable(ax)
966
967    if use_axes is None:
968        use_axes = ax
969
970    divider.add_auto_adjustable_area(use_axes=use_axes, pad=pad,
971                                     adjust_dirs=adjust_dirs)
972
973#from matplotlib.axes import Axes
974from .mpl_axes import Axes
975LocatableAxes = locatable_axes_factory(Axes)
976