From: <lee...@us...> - 2010-12-29 05:22:03
|
Revision: 8850 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=8850&view=rev Author: leejjoon Date: 2010-12-29 05:21:56 +0000 (Wed, 29 Dec 2010) Log Message: ----------- implement axes_divider.HBox and VBox Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/mpl_toolkits/axes_grid1/axes_divider.py trunk/matplotlib/lib/mpl_toolkits/axes_grid1/axes_grid.py trunk/matplotlib/lib/mpl_toolkits/axes_grid1/axes_size.py Added Paths: ----------- trunk/matplotlib/examples/axes_grid/demo_axes_hbox_divider.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2010-12-29 03:01:10 UTC (rev 8849) +++ trunk/matplotlib/CHANGELOG 2010-12-29 05:21:56 UTC (rev 8850) @@ -1,3 +1,5 @@ +2010-12-29 Implment axes_divider.HBox and VBox. -JJL + 2010-11-22 Fixed error with Hammer projection. - BVR 2010-11-12 Fixed the placement and angle of axis labels in 3D plots. - BVR Added: trunk/matplotlib/examples/axes_grid/demo_axes_hbox_divider.py =================================================================== --- trunk/matplotlib/examples/axes_grid/demo_axes_hbox_divider.py (rev 0) +++ trunk/matplotlib/examples/axes_grid/demo_axes_hbox_divider.py 2010-12-29 05:21:56 UTC (rev 8850) @@ -0,0 +1,48 @@ +import numpy as np +import matplotlib.pyplot as plt +from mpl_toolkits.axes_grid1.axes_divider import HBoxDivider +import mpl_toolkits.axes_grid1.axes_size as Size + +def make_heights_equal(fig, ax1, ax2, pad): + # pad in inches + + h1, v1 = Size.AxesX(ax1), Size.AxesY(ax1) + h2, v2 = Size.AxesX(ax2), Size.AxesY(ax2) + + pad_v = Size.Scaled(1) + pad_h = Size.Fixed(pad) + + my_divider = HBoxDivider(fig, 111, + horizontal=[h1, pad_h, h2], + vertical=[v1, pad_v, v2]) + + + ax1.set_axes_locator(my_divider.new_locator(0)) + ax2.set_axes_locator(my_divider.new_locator(2)) + + +if __name__ == "__main__": + + fig1 = plt.figure() + + arr1 = np.arange(20).reshape((4,5)) + arr2 = np.arange(20).reshape((5,4)) + + ax1 = plt.subplot(121) + ax2 = plt.subplot(122) + + ax1.imshow(arr1, interpolation="nearest") + ax2.imshow(arr2, interpolation="nearest") + + make_heights_equal(fig1, ax1, ax2, pad=0.5) + + for ax in [ax1, ax2]: + ax.locator_params(nbins=4) + + # annotate + ax3 = plt.axes([0.5, 0.5, 0.001, 0.001], frameon=False) + ax3.xaxis.set_visible(False) + ax3.yaxis.set_visible(False) + ax3.annotate("Location of two axes are adjusted\n so that they have an equal height\n while maintaining their aspect ratios", (0.5, 0.5), + xycoords="axes fraction", va="center", ha="center", + bbox=dict(fc="w")) Modified: trunk/matplotlib/lib/mpl_toolkits/axes_grid1/axes_divider.py =================================================================== --- trunk/matplotlib/lib/mpl_toolkits/axes_grid1/axes_divider.py 2010-12-29 03:01:10 UTC (rev 8849) +++ trunk/matplotlib/lib/mpl_toolkits/axes_grid1/axes_divider.py 2010-12-29 05:21:56 UTC (rev 8850) @@ -59,15 +59,30 @@ self._aspect = aspect self._xrefindex = 0 self._yrefindex = 0 + self._locator = None + def get_horizontal_sizes(self, renderer): + return [s.get_size(renderer) for s in self.get_horizontal()] + def get_vertical_sizes(self, renderer): + return [s.get_size(renderer) for s in self.get_vertical()] + + def get_vsize_hsize(self): + + from axes_size import AddList + + vsize = AddList(self.get_vertical()) + hsize = AddList(self.get_horizontal()) + + return vsize, hsize + + @staticmethod - def _calc_k(l, total_size, renderer): + def _calc_k(l, total_size): rs_sum, as_sum = 0., 0. - for s in l: - _rs, _as = s.get_size(renderer) + for _rs, _as in l: rs_sum += _rs as_sum += _as @@ -79,12 +94,13 @@ @staticmethod - def _calc_offsets(l, k, renderer): + def _calc_offsets(l, k): offsets = [0.] - for s in l: - _rs, _as = s.get_size(renderer) + #for s in l: + for _rs, _as in l: + #_rs, _as = s.get_size(renderer) offsets.append(offsets[-1] + _rs*k + _as) return offsets @@ -168,8 +184,19 @@ "return aspect" return self._aspect + def set_locator(self, _locator): + self._locator = _locator - def locate(self, nx, ny, nx1=None, ny1=None, renderer=None): + def get_locator(self): + return self._locator + + def get_position_runtime(self, ax, renderer): + if self._locator is None: + return self.get_position() + else: + return self._locator(ax, renderer).bounds + + def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None): """ :param nx, nx1: Integers specifying the column-position of the @@ -182,15 +209,17 @@ figW,figH = self._fig.get_size_inches() - x, y, w, h = self.get_position() + x, y, w, h = self.get_position_runtime(axes, renderer) - k_h = self._calc_k(self._horizontal, figW*w, renderer) - k_v = self._calc_k(self._vertical, figH*h, renderer) + hsizes = self.get_horizontal_sizes(renderer) + vsizes = self.get_vertical_sizes(renderer) + k_h = self._calc_k(hsizes, figW*w) + k_v = self._calc_k(vsizes, figH*h) if self.get_aspect(): k = min(k_h, k_v) - ox = self._calc_offsets(self._horizontal, k, renderer) - oy = self._calc_offsets(self._vertical, k, renderer) + ox = self._calc_offsets(hsizes, k) + oy = self._calc_offsets(vsizes, k) ww = (ox[-1] - ox[0])/figW hh = (oy[-1] - oy[0])/figH @@ -200,8 +229,8 @@ x0, y0 = pb1_anchored.x0, pb1_anchored.y0 else: - ox = self._calc_offsets(self._horizontal, k_h, renderer) - oy = self._calc_offsets(self._vertical, k_v, renderer) + ox = self._calc_offsets(hsizes, k_h) + oy = self._calc_offsets(vsizes, k_v) x0, y0 = x, y @@ -274,6 +303,7 @@ self._ny + _yrefindex, self._nx1 + _xrefindex, self._ny1 + _yrefindex, + axes, renderer) @@ -378,13 +408,20 @@ Divider based on the pre-existing axes. """ - def __init__(self, axes): + def __init__(self, axes, xref=None, yref=None): """ :param axes: axes """ self._axes = axes - self._xref = Size.AxesX(axes) - self._yref = Size.AxesY(axes) + if xref==None: + self._xref = Size.AxesX(axes) + else: + self._xref = xref + if yref==None: + self._yref = Size.AxesY(axes) + else: + self._yref = yref + Divider.__init__(self, fig=axes.get_figure(), pos=None, horizontal=[self._xref], vertical=[self._yref], aspect=None, anchor="C") @@ -553,203 +590,255 @@ -class LocatableAxesBase: - def __init__(self, *kl, **kw): - self._axes_class.__init__(self, *kl, **kw) - self._locator = None - self._locator_renderer = None - def set_axes_locator(self, locator): - self._locator = locator +class HBoxDivider(SubplotDivider): - def get_axes_locator(self): - return self._locator - def apply_aspect(self, position=None): + def __init__(self, fig, *args, **kwargs): + SubplotDivider.__init__(self, fig, *args, **kwargs) - if self.get_axes_locator() is None: - self._axes_class.apply_aspect(self, position) - else: - pos = self.get_axes_locator()(self, self._locator_renderer) - self._axes_class.apply_aspect(self, position=pos) + @staticmethod + def _determine_karray(equivalent_sizes, appended_sizes, + max_equivalent_size, + total_appended_size): - def draw(self, renderer=None, inframe=False): - self._locator_renderer = renderer + n = len(equivalent_sizes) + import numpy as np + A = np.mat(np.zeros((n+1, n+1), dtype="d")) + B = np.zeros((n+1), dtype="d") + # AxK = B - self._axes_class.draw(self, renderer, inframe) + # populated A + for i, (r, a) in enumerate(equivalent_sizes): + A[i,i] = r + A[i,-1] = -1 + B[i] = -a + A[-1,:-1] = [r for r, a in appended_sizes] + B[-1] = total_appended_size - sum([a for rs, a in appended_sizes]) + karray_H = (A.I*np.mat(B).T).A1 + karray = karray_H[:-1] + H = karray_H[-1] + if H > max_equivalent_size: + karray = (max_equivalent_size - \ + np.array([a for r, a in equivalent_sizes])) \ + / np.array([r for r, a in equivalent_sizes]) + return karray -_locatableaxes_classes = {} -def locatable_axes_factory(axes_class): - new_class = _locatableaxes_classes.get(axes_class) - if new_class is None: - new_class = new.classobj("Locatable%s" % (axes_class.__name__), - (LocatableAxesBase, axes_class), - {'_axes_class': axes_class}) - _locatableaxes_classes[axes_class] = new_class + @staticmethod + def _calc_offsets(appended_sizes, karray): + offsets = [0.] - return new_class + #for s in l: + for (r, a), k in zip(appended_sizes, karray): + offsets.append(offsets[-1] + r*k + a) -#if hasattr(maxes.Axes, "get_axes_locator"): -# LocatableAxes = maxes.Axes -#else: + return offsets -def make_axes_locatable(axes): - if not hasattr(axes, "set_axes_locator"): - new_class = locatable_axes_factory(type(axes)) - axes.__class__ = new_class - divider = AxesDivider(axes) - locator = divider.new_locator(nx=0, ny=0) - axes.set_axes_locator(locator) + def new_locator(self, nx, nx1=None): + """ + returns a new locator + (:class:`mpl_toolkits.axes_grid.axes_divider.AxesLocator`) for + specified cell. - return divider + :param nx, nx1: Integers specifying the column-position of the + cell. When nx1 is None, a single nx-th column is + specified. Otherwise location of columns spanning between nx + to nx1 (but excluding nx1-th column) is specified. + :param ny, ny1: same as nx and nx1, but for row positions. + """ + return AxesLocator(self, nx, 0, nx1, None) -#from matplotlib.axes import Axes -from mpl_axes import Axes -LocatableAxes = locatable_axes_factory(Axes) + def _locate(self, x, y, w, h, + y_equivalent_sizes, x_appended_sizes, + figW, figH): + """ + :param nx, nx1: Integers specifying the column-position of the + cell. When nx1 is None, a single nx-th column is + specified. Otherwise location of columns spanning between nx + to nx1 (but excluding nx1-th column) is specified. -def get_demo_image(): - # prepare image - delta = 0.5 + :param ny, ny1: same as nx and nx1, but for row positions. + """ - extent = (-3,4,-4,3) - import numpy as np - x = np.arange(-3.0, 4.001, delta) - y = np.arange(-4.0, 3.001, delta) - X, Y = np.meshgrid(x, y) - import matplotlib.mlab as mlab - Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) - Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) - Z = (Z1 - Z2) * 10 - return Z, extent + equivalent_sizes = y_equivalent_sizes + appended_sizes = x_appended_sizes -def demo_locatable_axes(): - import matplotlib.pyplot as plt + max_equivalent_size = figH*h + total_appended_size = figW*w + karray = self._determine_karray(equivalent_sizes, appended_sizes, + max_equivalent_size, + total_appended_size) - fig1 = plt.figure(1, (6, 6)) - fig1.clf() + ox = self._calc_offsets(appended_sizes, karray) - ## PLOT 1 - # simple image & colorbar - ax = fig1.add_subplot(2, 2, 1) + ww = (ox[-1] - ox[0])/figW + ref_h = equivalent_sizes[0] + hh = (karray[0]*ref_h[0] + ref_h[1])/figH + pb = mtransforms.Bbox.from_bounds(x, y, w, h) + pb1 = mtransforms.Bbox.from_bounds(x, y, ww, hh) + pb1_anchored = pb1.anchored(self.get_anchor(), pb) + x0, y0 = pb1_anchored.x0, pb1_anchored.y0 - Z, extent = get_demo_image() + return x0, y0, ox, hh - im = ax.imshow(Z, extent=extent, interpolation="nearest") - cb = plt.colorbar(im) - plt.setp(cb.ax.get_yticklabels(), visible=False) + def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None): + """ + :param nx, nx1: Integers specifying the column-position of the + cell. When nx1 is None, a single nx-th column is + specified. Otherwise location of columns spanning between nx + to nx1 (but excluding nx1-th column) is specified. - ## PLOT 2 - # image and colorbar whose location is adjusted in the drawing time. - # a hard way + :param ny, ny1: same as nx and nx1, but for row positions. + """ - divider = SubplotDivider(fig1, 2, 2, 2, aspect=True) - # axes for image - ax = LocatableAxes(fig1, divider.get_position()) + figW,figH = self._fig.get_size_inches() + x, y, w, h = self.get_position_runtime(axes, renderer) - # axes for coloarbar - ax_cb = LocatableAxes(fig1, divider.get_position()) + y_equivalent_sizes = self.get_vertical_sizes(renderer) + x_appended_sizes = self.get_horizontal_sizes(renderer) + x0, y0, ox, hh = self._locate(x, y, w, h, + y_equivalent_sizes, x_appended_sizes, + figW, figH) + if nx1 is None: + nx1=nx+1 - h = [Size.AxesX(ax), # main axes - Size.Fixed(0.05), # padding, 0.1 inch - Size.Fixed(0.2), # colorbar, 0.3 inch - ] + x1, w1 = x0 + ox[nx]/figW, (ox[nx1] - ox[nx])/figW + y1, h1 = y0, hh - v = [Size.AxesY(ax)] + return mtransforms.Bbox.from_bounds(x1, y1, w1, h1) - divider.set_horizontal(h) - divider.set_vertical(v) - ax.set_axes_locator(divider.new_locator(nx=0, ny=0)) - ax_cb.set_axes_locator(divider.new_locator(nx=2, ny=0)) - fig1.add_axes(ax) - fig1.add_axes(ax_cb) +class VBoxDivider(HBoxDivider): + """ + The Divider class whose rectangle area is specified as a subplot grometry. + """ - ax_cb.yaxis.set_ticks_position("right") - Z, extent = get_demo_image() + def new_locator(self, ny, ny1=None): + """ + returns a new locator + (:class:`mpl_toolkits.axes_grid.axes_divider.AxesLocator`) for + specified cell. - im = ax.imshow(Z, extent=extent, interpolation="nearest") - plt.colorbar(im, cax=ax_cb) - plt.setp(ax_cb.get_yticklabels(), visible=False) + :param nx, nx1: Integers specifying the column-position of the + cell. When nx1 is None, a single nx-th column is + specified. Otherwise location of columns spanning between nx + to nx1 (but excluding nx1-th column) is specified. - plt.draw() - #plt.colorbar(im, cax=ax_cb) + :param ny, ny1: same as nx and nx1, but for row positions. + """ + return AxesLocator(self, 0, ny, None, ny1) - ## PLOT 3 - # image and colorbar whose location is adjusted in the drawing time. - # a easy way + def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None): + """ - ax = fig1.add_subplot(2, 2, 3) - divider = make_axes_locatable(ax) + :param nx, nx1: Integers specifying the column-position of the + cell. When nx1 is None, a single nx-th column is + specified. Otherwise location of columns spanning between nx + to nx1 (but excluding nx1-th column) is specified. - ax_cb = divider.new_horizontal(size="5%", pad=0.05) - fig1.add_axes(ax_cb) + :param ny, ny1: same as nx and nx1, but for row positions. + """ - im = ax.imshow(Z, extent=extent, interpolation="nearest") - plt.colorbar(im, cax=ax_cb) - plt.setp(ax_cb.get_yticklabels(), visible=False) + figW,figH = self._fig.get_size_inches() + x, y, w, h = self.get_position_runtime(axes, renderer) - ## PLOT 4 - # two images side by sied with fixed padding. + x_equivalent_sizes = self.get_horizontal_sizes(renderer) + y_appended_sizes = self.get_vertical_sizes(renderer) - ax = fig1.add_subplot(2, 2, 4) - divider = make_axes_locatable(ax) + y0, x0, oy, ww = self._locate(y, x, h, w, + x_equivalent_sizes, y_appended_sizes, + figH, figW) + if ny1 is None: + ny1=ny+1 - ax2 = divider.new_horizontal(size="100%", pad=0.05) - fig1.add_axes(ax2) + x1, w1 = x0, ww + y1, h1 = y0 + oy[ny]/figH, (oy[ny1] - oy[ny])/figH - ax.imshow(Z, extent=extent, interpolation="nearest") - ax2.imshow(Z, extent=extent, interpolation="nearest") - plt.setp(ax2.get_yticklabels(), visible=False) - plt.draw() - plt.show() + return mtransforms.Bbox.from_bounds(x1, y1, w1, h1) -def demo_fixed_size_axes(): - import matplotlib.pyplot as plt - fig2 = plt.figure(2, (6, 6)) - # The first items are for padding and the second items are for the axes. - # sizes are in inch. - h = [Size.Fixed(1.0), Size.Fixed(4.5)] - v = [Size.Fixed(0.7), Size.Fixed(5.)] - divider = Divider(fig2, (0.0, 0.0, 1., 1.), h, v, aspect=False) - # the width and height of the rectangle is ignored. +class LocatableAxesBase: + def __init__(self, *kl, **kw): - ax = LocatableAxes(fig2, divider.get_position()) - ax.set_axes_locator(divider.new_locator(nx=1, ny=1)) + self._axes_class.__init__(self, *kl, **kw) - fig2.add_axes(ax) + self._locator = None + self._locator_renderer = None - ax.plot([1,2,3]) + def set_axes_locator(self, locator): + self._locator = locator - plt.draw() - plt.show() - #plt.colorbar(im, cax=ax_cb) + def get_axes_locator(self): + return self._locator + def apply_aspect(self, position=None): + if self.get_axes_locator() is None: + self._axes_class.apply_aspect(self, position) + else: + pos = self.get_axes_locator()(self, self._locator_renderer) + self._axes_class.apply_aspect(self, position=pos) + def draw(self, renderer=None, inframe=False): -if __name__ == "__main__": - demo_locatable_axes() - demo_fixed_size_axes() + self._locator_renderer = renderer + + self._axes_class.draw(self, renderer, inframe) + + + +_locatableaxes_classes = {} +def locatable_axes_factory(axes_class): + + new_class = _locatableaxes_classes.get(axes_class) + if new_class is None: + new_class = new.classobj("Locatable%s" % (axes_class.__name__), + (LocatableAxesBase, axes_class), + {'_axes_class': axes_class}) + _locatableaxes_classes[axes_class] = new_class + + return new_class + +#if hasattr(maxes.Axes, "get_axes_locator"): +# LocatableAxes = maxes.Axes +#else: + +def make_axes_locatable(axes): + if not hasattr(axes, "set_axes_locator"): + new_class = locatable_axes_factory(type(axes)) + axes.__class__ = new_class + + divider = AxesDivider(axes) + locator = divider.new_locator(nx=0, ny=0) + axes.set_axes_locator(locator) + + return divider + + +#from matplotlib.axes import Axes +from mpl_axes import Axes +LocatableAxes = locatable_axes_factory(Axes) + + Modified: trunk/matplotlib/lib/mpl_toolkits/axes_grid1/axes_grid.py =================================================================== --- trunk/matplotlib/lib/mpl_toolkits/axes_grid1/axes_grid.py 2010-12-29 03:01:10 UTC (rev 8849) +++ trunk/matplotlib/lib/mpl_toolkits/axes_grid1/axes_grid.py 2010-12-29 05:21:56 UTC (rev 8850) @@ -413,7 +413,25 @@ ax = self.axes_llc _tick_only(ax, bottom_on=False, left_on=False) + def set_axes_locator(self, locator): + self._divider.set_locator(locator) + def get_axes_locator(self): + return self._divider.get_locator() + + def get_vsize_hsize(self): + + return self._divider.get_vsize_hsize() +# from axes_size import AddList + +# vsize = AddList(self._divider.get_vertical()) +# hsize = AddList(self._divider.get_horizontal()) + +# return vsize, hsize + + + + class ImageGrid(Grid): """ A class that creates a grid of Axes. In matplotlib, the axes Modified: trunk/matplotlib/lib/mpl_toolkits/axes_grid1/axes_size.py =================================================================== --- trunk/matplotlib/lib/mpl_toolkits/axes_grid1/axes_size.py 2010-12-29 03:01:10 UTC (rev 8849) +++ trunk/matplotlib/lib/mpl_toolkits/axes_grid1/axes_size.py 2010-12-29 05:21:56 UTC (rev 8850) @@ -16,8 +16,40 @@ class _Base(object): "Base class" - pass + def __rmul__(self, other): + float(other) # just to check if number if given + return Fraction(other, self) + + def __add__(self, other): + if isinstance(other, _Base): + return Add(self, other) + else: + float(other) + other = Fixed(other) + return Add(self, other) + + +class Add(_Base): + def __init__(self, a, b): + self._a = a + self._b = b + + def get_size(self, renderer): + a_rel_size, a_abs_size = self._a.get_size(renderer) + b_rel_size, b_abs_size = self._b.get_size(renderer) + return a_rel_size + b_rel_size, a_abs_size + b_abs_size + +class AddList(_Base): + def __init__(self, add_list): + self._list = add_list + + def get_size(self, renderer): + sum_rel_size = sum([a.get_size(renderer)[0] for a in self._list]) + sum_abs_size = sum([a.get_size(renderer)[1] for a in self._list]) + return sum_rel_size, sum_abs_size + + class Fixed(_Base): "Simple fixed size with absolute part = *fixed_size* and relative part = 0" def __init__(self, fixed_size): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |