From: <lee...@us...> - 2009-06-03 05:50:06
|
Revision: 7175 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=7175&view=rev Author: leejjoon Date: 2009-06-03 05:49:59 +0000 (Wed, 03 Jun 2009) Log Message: ----------- axes_grid: initial checkin of curvelinear grid support Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/mpl_toolkits/axes_grid/ChangeLog trunk/matplotlib/lib/mpl_toolkits/axes_grid/axislines.py Added Paths: ----------- trunk/matplotlib/examples/axes_grid/demo_curvelinear_grid.py trunk/matplotlib/lib/mpl_toolkits/axes_grid/angle_helper.py trunk/matplotlib/lib/mpl_toolkits/axes_grid/clip_path.py trunk/matplotlib/lib/mpl_toolkits/axes_grid/grid_finder.py trunk/matplotlib/lib/mpl_toolkits/axes_grid/grid_helper_curvelinear.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2009-06-02 02:30:57 UTC (rev 7174) +++ trunk/matplotlib/CHANGELOG 2009-06-03 05:49:59 UTC (rev 7175) @@ -1,3 +1,6 @@ +2009-06-03 axes_grid : Initial check-in of curvelinear grid support. See + examples/axes_grid/demo_curvelinear_grid.py - JJL + 2009-06-01 Spine is now derived from Patch - ADS 2009-06-01 use cbook.is_string_like() instead of isinstance() for spines - ADS Added: trunk/matplotlib/examples/axes_grid/demo_curvelinear_grid.py =================================================================== --- trunk/matplotlib/examples/axes_grid/demo_curvelinear_grid.py (rev 0) +++ trunk/matplotlib/examples/axes_grid/demo_curvelinear_grid.py 2009-06-03 05:49:59 UTC (rev 7175) @@ -0,0 +1,131 @@ +import numpy as np +#from matplotlib.path import Path + +import matplotlib.pyplot as plt +import matplotlib.cbook as cbook + +from mpl_toolkits.axes_grid.grid_helper_curvelinear import GridHelperCurveLinear +from mpl_toolkits.axes_grid.axislines import Subplot + +from mpl_toolkits.axes_grid.parasite_axes import SubplotHost, \ + ParasiteAxesAuxTrans + + +def curvelinear_test1(fig): + """ + grid for custom transform. + """ + + def tr(x, y): + x, y = np.asarray(x), np.asarray(y) + return x, y-x + + def inv_tr(x,y): + x, y = np.asarray(x), np.asarray(y) + return x, y+x + + + grid_helper = GridHelperCurveLinear((tr, inv_tr)) + + ax1 = Subplot(fig, 1, 2, 1, grid_helper=grid_helper) + # ax1 will have a ticks and gridlines defined by the given + # transform (+ transData of the Axes). Note that the transform of + # the Axes itself (i.e., transData) is not affected by the given + # transform. + + fig.add_subplot(ax1) + + xx, yy = tr([3, 6], [5.0, 10.]) + ax1.plot(xx, yy) + + ax1.set_aspect(1.) + ax1.set_xlim(0, 10.) + ax1.set_ylim(0, 10.) + + ax1.grid(True) + + + +import mpl_toolkits.axes_grid.angle_helper as angle_helper +from matplotlib.projections import PolarAxes +from matplotlib.transforms import Affine2D + +def curvelinear_test2(fig): + """ + polar projection, but in a rectangular box. + """ + + # PolarAxes.PolarTransform takes radian. However, we want our coordinate + # system in degree + tr = Affine2D().scale(np.pi/180., 1.) + PolarAxes.PolarTransform() + + # polar projection, which involves cycle, and also has limits in + # its coordinates, needs a special method to find the extremes + # (min, max of the coordinate within the view). + + # 20, 20 : number of sampling points along x, y direction + extreme_finder = angle_helper.ExtremeFinderCycle(20, 20, + lon_cycle = 360, + lat_cycle = None, + lon_minmax = None, + lat_minmax = (0, np.inf), + ) + + grid_locator1 = angle_helper.LocatorDMS(12) + # Find a grid values appropriate for the coordinate (degree, + # minute, second). + + tick_formatter1 = angle_helper.FormatterDMS() + # And also uses an appropriate formatter. Note that,the + # acceptable Locator and Formatter class is a bit different than + # that of mpl's, and you cannot directly use mpl's Locator and + # Formatter here (but may be possible in the future). + + grid_helper = GridHelperCurveLinear(tr, + extreme_finder=extreme_finder, + grid_locator1=grid_locator1, + tick_formatter1=tick_formatter1 + ) + + + ax1 = SubplotHost(fig, 1, 2, 2, grid_helper=grid_helper) + + # make ticklabels of right and top axis visible. + ax1.axis["right"].major_ticklabels.set_visible(True) + ax1.axis["top"].major_ticklabels.set_visible(True) + + # let right axis shows ticklabels for 1st coordinate (angle) + ax1.axis["right"].get_helper().nth_coord_ticks=0 + # let bottom axis shows ticklabels for 2nd coordinate (radius) + ax1.axis["bottom"].get_helper().nth_coord_ticks=1 + + fig.add_subplot(ax1) + + + # A parasite axes with given transform + ax2 = ParasiteAxesAuxTrans(ax1, tr, "equal") + # note that ax2.transData == tr + ax1.transData + # Anthing you draw in ax2 will match the ticks and grids of ax1. + ax1.parasites.append(ax2) + intp = cbook.simple_linear_interpolation + ax2.plot(intp(np.array([0, 30]), 50), + intp(np.array([10., 10.]), 50)) + + ax1.set_aspect(1.) + ax1.set_xlim(-5, 12) + ax1.set_ylim(-5, 10) + + ax1.grid(True) + +if 1: + fig = plt.figure(1, figsize=(7, 4)) + fig.clf() + + curvelinear_test1(fig) + curvelinear_test2(fig) + + plt.draw() + plt.show() + + + Modified: trunk/matplotlib/lib/mpl_toolkits/axes_grid/ChangeLog =================================================================== --- trunk/matplotlib/lib/mpl_toolkits/axes_grid/ChangeLog 2009-06-02 02:30:57 UTC (rev 7174) +++ trunk/matplotlib/lib/mpl_toolkits/axes_grid/ChangeLog 2009-06-03 05:49:59 UTC (rev 7175) @@ -1,6 +1,7 @@ 2009-06-01 Jae-Joon Lee <lee...@gm...> * axislines.py (Axes.toggle_axisline): fix broken spine support. + (AxisArtistHelper): Initial support for curvelinear grid and ticks. 2009-05-04 Jae-Joon Lee <lee...@gm...> Added: trunk/matplotlib/lib/mpl_toolkits/axes_grid/angle_helper.py =================================================================== --- trunk/matplotlib/lib/mpl_toolkits/axes_grid/angle_helper.py (rev 0) +++ trunk/matplotlib/lib/mpl_toolkits/axes_grid/angle_helper.py 2009-06-03 05:49:59 UTC (rev 7175) @@ -0,0 +1,345 @@ +from math import floor + +import numpy as np +import math + +A = np.array + +from mpl_toolkits.axes_grid.grid_finder import ExtremeFinderSimple + +def select_step_degree(dv): + + degree_limits_ = [1.5, 3, 7, 13, 20, 40, 70, 120, 270, 520] + degree_steps_ = [ 1, 2, 5, 10, 15, 30, 45, 90, 180, 360] + degree_factors = [1.] * len(degree_steps_) + + minsec_limits_ = [1.5, 2.5, 3.5, 8, 11, 18, 25, 45] + minsec_steps_ = [1, 2, 3, 5, 10, 15, 20, 30] + + minute_limits_ = A(minsec_limits_)*(1./60.) + minute_factors = [60.] * len(minute_limits_) + + second_limits_ = A(minsec_limits_)*(1./3600.) + second_factors = [3600.] * len(second_limits_) + + degree_limits = np.concatenate([second_limits_, + minute_limits_, + degree_limits_]) + + degree_steps = np.concatenate([minsec_steps_, + minsec_steps_, + degree_steps_]) + + degree_factors = np.concatenate([second_factors, + minute_factors, + degree_factors]) + + n = degree_limits.searchsorted(dv) + step = degree_steps[n] + factor = degree_factors[n] + + return step, factor + + + +def select_step_hour(dv): + + hour_limits_ = [1.5, 2.5, 3.5, 5, 7, 10, 15, 21, 36] + hour_steps_ = [1, 2 , 3, 4, 6, 8, 12, 18, 24] + hour_factors = [1.] * len(hour_steps_) + + minsec_limits_ = [1.5, 2.5, 3.5, 4.5, 5.5, 8, 11, 14, 18, 25, 45] + minsec_steps_ = [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30] + + minute_limits_ = A(minsec_limits_)*(1./60.) + minute_factors = [60.] * len(minute_limits_) + + second_limits_ = A(minsec_limits_)*(1./3600.) + second_factors = [3600.] * len(second_limits_) + + hour_limits = np.concatenate([second_limits_, + minute_limits_, + hour_limits_]) + + hour_steps = np.concatenate([minsec_steps_, + minsec_steps_, + hour_steps_]) + + hour_factors = np.concatenate([second_factors, + minute_factors, + hour_factors]) + + n = hour_limits.searchsorted(dv) + step = hour_steps[n] + factor = hour_factors[n] + + return step, factor + + +def select_step_sub(dv): + + # subarcsec or degree + tmp = 10.**(int(math.log10(dv))-1.) + dv2 = dv/tmp + substep_limits_ = [1.5, 3., 7.] + substep_steps_ = [1. , 2., 5.] + + factor = 1./tmp + + if 1.5*tmp >= dv: + step = 1 + elif 3.*tmp >= dv: + step = 2 + elif 7.*tmp >= dv: + step = 5 + else: + step = 1 + factor = 0.1*factor + + return step, factor + + +def select_step(v1, v2, nv, hour=False): + + if v1 > v2: + v1, v2 = v2, v1 + + A = np.array + + dv = float(v2 - v1) / nv + + if hour: + _select_step = select_step_hour + cycle = 24. + else: + _select_step = select_step_degree + cycle = 360. + + # for degree + if dv > 1./3600.: + #print "degree" + step, factor = _select_step(dv) + else: + step, factor = select_step_sub(dv*3600.) + #print "feac", step, factor + + factor = factor * 3600. + + + f1, f2, fstep = v1*factor, v2*factor, step/factor + levs = np.arange(math.floor(f1/step), math.ceil(f2/step)+0.5, + 1, dtype="i") * step + + # n : number valid levels. If there is a cycle, e.g., [0, 90, 180, + # 270, 360], the a grid line needs to be extend from 0 to 360, so + # we need to return the whole array. However, the last level (360) + # needs to be ignored often. In this case, so we return n=4. + + n = len(levs) + + + # we need to check the range of values + # for example, -90 to 90, 0 to 360, + + + if factor == 1. and (levs[-1] >= levs[0]+cycle): # check for cycle + nv = int(cycle / step) + levs = np.arange(0, nv, 1) * step + n = len(levs) + + return levs, n, factor + + +def select_step24(v1, v2, nv): + v1, v2 = v1/15., v2/15. + levs, n, factor = select_step(v1, v2, nv, hour=True) + return levs*15., n, factor + +def select_step360(v1, v2, nv): + return select_step(v1, v2, nv, hour=False) + + + + +class LocatorHMS(object): + def __init__(self, den): + self.den = den + def __call__(self, v1, v2): + return select_step24(v1, v2, self.den) + + +class LocatorDMS(object): + def __init__(self, den): + self.den = den + def __call__(self, v1, v2): + return select_step360(v1, v2, self.den) + + +class FormatterHMS(object): + def __call__(self, direction, factor, values): # hour + if len(values) == 0: + return [] + ss = [[-1, 1][v>0] for v in values] + values = np.abs(values)/15. + + if factor == 1: + return ["$%d^{\mathrm{h}}$" % (int(v),) for v in values] + elif factor == 60: + return ["$%d^{\mathrm{h}}\,%02d^{\mathrm{m}}$" % (s*floor(v/60.), v%60) \ + for s, v in zip(ss, values)] + elif factor == 3600: + if ss[-1] == -1: + inverse_order = True + values = values[::-1] + else: + inverse_order = False + degree = floor(values[0]/3600.) + hm_fmt = "$%d^{\mathrm{h}}\,%02d^{\mathrm{m}}\," + s_fmt = "%02d^{\mathrm{s}}$" + l_hm_old = "" + r = [] + for v in values-3600*degree: + l_hm = hm_fmt % (ss[0]*degree, floor(v/60.)) + l_s = s_fmt % (v%60,) + if l_hm != l_hm_old: + l_hm_old = l_hm + l = l_hm + l_s + else: + l = "$"+l_s + r.append(l) + if inverse_order: + return r[::-1] + else: + return r + #return [fmt % (ss[0]*degree, floor(v/60.), v%60) \ + # for s, v in zip(ss, values-3600*degree)] + else: # factor > 3600. + return ["$%s$" % (str(v),) for v in values] + + +class FormatterDMS(object): + def __call__(self, direction, factor, values): + if len(values) == 0: + return [] + ss = [[-1, 1][v>0] for v in values] + values = np.abs(values) + if factor == 1: + return ["$%d^{\circ}$" % (s*int(v),) for (s, v) in zip(ss, values)] + elif factor == 60: + return ["$%d^{\circ}\,%02d^{\prime}$" % (s*floor(v/60.), v%60) \ + for s, v in zip(ss, values)] + elif factor == 3600: + if ss[-1] == -1: + inverse_order = True + values = values[::-1] + else: + inverse_order = False + degree = floor(values[0]/3600.) + hm_fmt = "$%d^{\circ}\,%02d^{\prime}\," + s_fmt = "%02d^{\prime\prime}$" + l_hm_old = "" + r = [] + for v in values-3600*degree: + l_hm = hm_fmt % (ss[0]*degree, floor(v/60.)) + l_s = s_fmt % (v%60,) + if l_hm != l_hm_old: + l_hm_old = l_hm + l = l_hm + l_s + else: + l = "$"+l_s + r.append(l) + if inverse_order: + return r[::-1] + else: + return r + #return [fmt % (ss[0]*degree, floor(v/60.), v%60) \ + # for s, v in zip(ss, values-3600*degree)] + else: # factor > 3600. + return ["$%s$" % (str(v),) for v in ss*values] + + + + +class ExtremeFinderCycle(ExtremeFinderSimple): + """ + When there is a cycle, e.g., longitude goes from 0-360. + """ + def __init__(self, + nx, ny, + lon_cycle = 360., + lat_cycle = None, + lon_minmax = None, + lat_minmax = (-90, 90) + ): + #self.transfrom_xy = transform_xy + #self.inv_transfrom_xy = inv_transform_xy + self.nx, self.ny = nx, ny + self.lon_cycle, self.lat_cycle = lon_cycle, lat_cycle + self.lon_minmax = lon_minmax + self.lat_minmax = lat_minmax + + + def __call__(self, transform_xy, x1, y1, x2, y2): + """ + get extreme values. + + x1, y1, x2, y2 in image coordinates (0-based) + nx, ny : number of dvision in each axis + """ + x_, y_ = np.linspace(x1, x2, self.nx), np.linspace(y1, y2, self.ny) + x, y = np.meshgrid(x_, y_) + lon, lat = transform_xy(np.ravel(x), np.ravel(y)) + + # iron out jumps, but algorithm should be improved. + # Tis is just naive way of doing and my fail for some cases. + if self.lon_cycle is not None: + lon0 = lon.min() + lon -= 360. * ((lon - lon0) > 180.) + if self.lat_cycle is not None: + lat0 = lat.min() + lat -= 360. * ((lat - lat0) > 180.) + + lon_min, lon_max = lon.min(), lon.max() + lat_min, lat_max = lat.min(), lat.max() + + lon_min, lon_max, lat_min, lat_max = \ + self._adjust_extremes(lon_min, lon_max, lat_min, lat_max) + + return lon_min, lon_max, lat_min, lat_max + + + def _adjust_extremes(self, lon_min, lon_max, lat_min, lat_max): + + lon_min, lon_max, lat_min, lat_max = \ + self._add_pad(lon_min, lon_max, lat_min, lat_max) + + # check cycle + if self.lon_cycle: + lon_max = min(lon_max, lon_min + self.lon_cycle) + if self.lat_cycle: + lat_max = min(lat_max, lat_min + self.lat_cycle) + + if self.lon_minmax is not None: + min0 = self.lon_minmax[0] + lon_min = max(min0, lon_min) + max0 = self.lon_minmax[1] + lon_max = min(max0, lon_max) + + if self.lat_minmax is not None: + min0 = self.lat_minmax[0] + lat_min = max(min0, lat_min) + max0 = self.lat_minmax[1] + lat_max = min(max0, lat_max) + + return lon_min, lon_max, lat_min, lat_max + + + + + +if __name__ == "__main__": + #test2() + print select_step360(21.2, 33.3, 5) + print select_step360(20+21.2/60., 21+33.3/60., 5) + print select_step360(20.5+21.2/3600., 20.5+33.3/3600., 5) + print select_step360(20+21.2/60., 20+53.3/60., 5) Modified: trunk/matplotlib/lib/mpl_toolkits/axes_grid/axislines.py =================================================================== --- trunk/matplotlib/lib/mpl_toolkits/axes_grid/axislines.py 2009-06-02 02:30:57 UTC (rev 7174) +++ trunk/matplotlib/lib/mpl_toolkits/axes_grid/axislines.py 2009-06-03 05:49:59 UTC (rev 7175) @@ -1,3 +1,48 @@ +""" +Axislines includes modified implementation of the Axes class. The +biggest difference is that the artists responsible to draw axis line, +ticks, ticklabel and axis labels are separated out from the mpl's Axis +class, which are much more than artists in the original +mpl. Originally, this change was motivated to support curvlinear +grid. Here are a few reasons that I came up with new axes class. + + + * "top" and "bottom" x-axis (or "left" and "right" y-axis) can have + different ticks (tick locations and labels). This is not possible + with the current mpl, although some twin axes trick can help. + + * Curvelinear grid. + + * angled ticks. + +In the new axes class, xaxis and yaxis is set to not visible by +default, and new set of artist (AxisArtist) are defined to draw axis +line, ticks, ticklabels and axis label. Axes.axis attribute serves as +a dictionary of these artists, i.e., ax.axis["left"] is a AxisArtist +instance responsible to draw left y-axis. The default Axes.axis contains +"bottom", "left", "top" and "right". + +AxisArtist can be considered as a container artist and +has following children artists which will draw ticks, labels, etc. + + * line + * major_ticks, major_ticklabels + * minor_ticks, minor_ticklabels + * offsetText + * label + +Note that these are separate artists from Axis class of the +original mpl, thus most of tick-related command in the original mpl +won't work, although some effort has made to work with. For example, +color and markerwidth of the ax.axis["bottom"].major_ticks will follow +those of Axes.xaxis unless explicitly specified. + +In addition to AxisArtist, the Axes will have *gridlines* attribute, +which obviously draws grid lines. The gridlines needs to be separated +from the axis as some gridlines can never pass any axis. + +""" + import matplotlib.axes as maxes import matplotlib.artist as martist import matplotlib.text as mtext @@ -73,25 +118,92 @@ class UnimplementedException(Exception): pass -class AxisLineHelper(object): + + +class AxisArtistHelper(object): + """ + AxisArtistHelper should define + following method with given APIs. Note that the first axes argument + will be axes attribute of the caller artist. + + + # LINE + + def get_line(self, axes): + # path : Path + return path + + def get_line_transform(self, axes): + # ... + # trans : transform + return trans + + # LABEL + + def get_label_pos(self, axes): + # x, y : position + return (x, y), trans + + + def get_label_offset_transform(self, \ + axes, + pad_points, fontprops, renderer, + bboxes, + ): + # va : vertical alignment + # ha : horizontal alignment + # a : angle + return trans, va, ha, a + + # TICK + + def get_tick_transform(self, axes): + return trans + + def get_tick_iterators(self, axes): + # iter : iteratoable object that yields (c, angle, l) where + # c, angle, l is position, tick angle, and label + + return iter_major, iter_minot + + + """ class _Base(object): + def __init__(self, label_direction): self.label_direction = label_direction - def get_label_pos(self): - raise UnimplementedException("") + #def update(self): + # raise UnimplementedException("update method not implemented") + def update_lim(self, axes): + pass + _label_angles = dict(left=90, right=90, bottom=0, top=0) _ticklabel_angles = dict(left=0, right=0, bottom=0, top=0) - def _get_label_transform(self, pad_points, fontprops, renderer, - bboxes=None, - trans=None): + def _get_label_offset_transform(self, pad_points, fontprops, renderer, + bboxes=None, + #trans=None + ): - if trans is None: - trans = self.axes.transAxes + """ + Returns (offset-transform, vertical-alignment, horiz-alignment) + of (tick or axis) labels appropriate for the label + direction. - #dpi_scale_trans = self.axes.figure.dpi_scale_trans + The offset-transform represents a required pixel offset + from the reference point. For example, x-axis center will + be the referece point for xlabel. + + pad_points : padding from axis line or tick labels (see bboxes) + fontprops : font properties for label + renderer : renderer + bboxes=None : list of bboxes (window extents) of the tick labels. + + all the above parameters are used to estimate the offset. + + """ if renderer: pad_pixels = renderer.points_to_pixels(pad_points) font_size_points = fontprops.get_size_in_points() @@ -111,74 +223,64 @@ tr = Affine2D() if self.label_direction == "left": tr.translate(-(pad_pixels+w), 0.) - trans = trans + tr + #trans = trans + tr - return trans, "center", "right" + return tr, "center", "right" elif self.label_direction == "right": tr.translate(+(pad_pixels+w), 0.) - #tr = ScaledTranslation(+((pad_points+w) / 72.), 0., - # dpi_scale_trans) - trans = trans + tr + #trans = trans + tr - return trans, "center", "left" + return tr, "center", "left" elif self.label_direction == "bottom": - #pad_points = font_size_points + pad_points tr.translate(0, -(pad_pixels+font_size_pixels+h)) - trans = trans + tr + #trans = trans + tr - return trans, "baseline", "center" + return tr, "baseline", "center" elif self.label_direction == "top": - #pad_points = font_size_points/8. + pad_points - #tr.translate(0, +(pad_pixels+font_size_pixels/6.+h)) - #tr.translate(0, +(pad_pixels+font_size_pixels/10.+h)) tr.translate(0, +(pad_pixels+h)) - #tr = ScaledTranslation(0, (pad_points+h) / 72., - # dpi_scale_trans) - trans = trans + tr + #trans = trans + tr - return trans, "baseline", "center" + return tr, "baseline", "center" else: raise ValueError("") - def get_label_transform(self, pad_points, fontprops, renderer, - bboxes, - trans=None): + def get_label_offset_transform(self, + axes, + pad_points, fontprops, renderer, + bboxes, + #trans=None + ): - tr, va, ha = self._get_label_transform(pad_points, fontprops, - renderer, - bboxes, trans) + tr, va, ha = self._get_label_offset_transform(pad_points, fontprops, + renderer, + bboxes, + #trans + ) a = self._label_angles[self.label_direction] return tr, va, ha, a - def get_ticklabel_transform(self, pad_points, fontprops, renderer, - trans=None): - tr, va, ha = self._get_label_transform(pad_points, fontprops, - renderer, - None, trans) + def get_ticklabel_offset_transform(self, axes, + pad_points, fontprops, + renderer, + ): + tr, va, ha = self._get_label_offset_transform(pad_points, fontprops, + renderer, + None, + ) + a = self._ticklabel_angles[self.label_direction] return tr, va, ha, a - def get_line_transform(self): - return self.axes.transAxes - def get_line(self): - raise UnimplementedException("") - def get_tick_transform(self): - raise UnimplementedException("") - - def get_tick_iterators(self): - raise UnimplementedException("") - - class Fixed(_Base): _default_passthru_pt = dict(left=(0, 0), @@ -186,13 +288,13 @@ bottom=(0, 0), top=(0, 1)) - def __init__(self, axes, loc, nth_coord=None, + def __init__(self, + loc, nth_coord=None, passingthrough_point=None, label_direction=None): """ nth_coord = along which coordinate value varies in 2d, nth_coord = 0 -> x axis, nth_coord = 1 -> y axis """ - self.axes = axes if loc not in ["left", "right", "bottom", "top"]: raise ValueError("%s" % loc) @@ -203,9 +305,8 @@ nth_coord = 0 self.nth_coord = nth_coord - self.axis = [self.axes.xaxis, self.axes.yaxis][self.nth_coord] - super(AxisLineHelper.Fixed, self).__init__(loc) + super(AxisArtistHelper.Fixed, self).__init__(loc) if passingthrough_point is None: passingthrough_point = self._default_passthru_pt[loc] @@ -220,113 +321,160 @@ fixed_coord = 1-nth_coord _verts[:,fixed_coord] = self.passthru_pt[fixed_coord] + # axis line in transAxes self._path = Path(_verts) def get_nth_coord(self): return self.nth_coord - def get_line(self): + # LINE + + def get_line(self, axes): return self._path - def get_label_pos(self): + def get_line_transform(self, axes): + return axes.transAxes + + # LABLE + + def get_label_pos(self, axes): + """ + label reference position in transAxes. + + get_label_transform() returns a transform of (transAxes+offset) + """ _verts = [0.5, 0.5] nth_coord = self.nth_coord fixed_coord = 1-nth_coord _verts[fixed_coord] = self.passthru_pt[fixed_coord] - return _verts, self.axes.transAxes + return _verts, axes.transAxes - def get_tick_transform(self): - trans_tick = [self.axes.get_xaxis_transform(), - self.axes.get_yaxis_transform()][self.nth_coord] - return trans_tick + def get_label_offset_transform(self, axes, + pad_points, fontprops, renderer, + bboxes, + ): - def get_tick_iterators(self): - """tick_loc, tick_angle, tick_label""" + tr, va, ha = self._get_label_offset_transform( \ + pad_points, fontprops, renderer, bboxes, + #trans + ) - angle = 0 - 90 * self.nth_coord - if self.passthru_pt[1 - self.nth_coord] > 0.5: - angle = 180+angle + a = self._label_angles[self.label_direction] + #tr = axes.transAxes + tr - major = self.axis.major - majorLocs = major.locator() - major.formatter.set_locs(majorLocs) - majorLabels = [major.formatter(val, i) for i, val in enumerate(majorLocs)] + return tr, va, ha, a - minor = self.axis.minor - minorLocs = minor.locator() - minor.formatter.set_locs(minorLocs) - minorLabels = [minor.formatter(val, i) for i, val in enumerate(minorLocs)] - trans_tick = self.get_tick_transform() - tr2ax = trans_tick + self.axes.transAxes.inverted() + # TICK - def _f(locs, labels): - for x, l in zip(locs, labels): + def get_tick_transform(self, axes): + trans_tick = [axes.get_xaxis_transform(), + axes.get_yaxis_transform()][self.nth_coord] - c = list(self.passthru_pt) # copy - c[self.nth_coord] = x + return trans_tick - c2 = tr2ax.transform_point(c) - delta=0.001 - if 0. -delta<= c2[self.nth_coord] <= 1.+delta: - yield c, angle, l - return _f(majorLocs, majorLabels), _f(minorLocs, minorLabels) - class Floating(_Base): - def __init__(self, axes, nth_coord, + def __init__(self, nth_coord, passingthrough_point, label_direction, transform): - self.axes = axes self.nth_coord = nth_coord - self.axis = [self.axes.xaxis, self.axes.yaxis][self.nth_coord] self.passingthrough_point = passingthrough_point self.transform = transform - super(AxisLineHelper.Floating, self).__init__(label_direction) + super(AxisArtistHelper.Floating, + self).__init__(label_direction) + def get_nth_coord(self): return self.nth_coord - def get_line(self): + def get_line(self, axes): _verts = np.array([[0., 0.], [1., 1.]]) fixed_coord = 1-self.nth_coord - trans_passingthrough_point = self.transform + self.axes.transAxes.inverted() + trans_passingthrough_point = self.transform + axes.transAxes.inverted() p = trans_passingthrough_point.transform_point(self.passingthrough_point) _verts[:,fixed_coord] = p[fixed_coord] return Path(_verts) + def get_line_transform(self, axes): + return axes.transAxes - def get_label_pos(self): + def get_label_pos(self, axes): _verts = [0.5, 0.5] fixed_coord = 1-self.nth_coord - trans_passingthrough_point = self.transform + self.axes.transAxes.inverted() + trans_passingthrough_point = self.transform + axes.transAxes.inverted() p = trans_passingthrough_point.transform_point(self.passingthrough_point) _verts[fixed_coord] = p[fixed_coord] if not (0. <= _verts[fixed_coord] <= 1.): return None, None else: - return _verts, self.axes.transAxes + return _verts, axes.transAxes + def get_label_transform(self, axes, + pad_points, fontprops, renderer, + bboxes, + ): + tr, va, ha = self._get_label_offset_transform(pad_points, fontprops, + renderer, + bboxes, + #trans + ) - def get_tick_transform(self): + a = self._label_angles[self.label_direction] + tr = axes.transAxes + tr + #tr = axes.transAxes + tr + + return tr, va, ha, a + + + + def get_tick_transform(self, axes): return self.transform - def get_tick_iterators(self): + + + + +class AxisArtistHelperRectlinear: + + class Fixed(AxisArtistHelper.Fixed): + + def __init__(self, + axes, loc, nth_coord=None, + passingthrough_point=None, label_direction=None): + """ + nth_coord = along which coordinate value varies + in 2d, nth_coord = 0 -> x axis, nth_coord = 1 -> y axis + """ + + super(AxisArtistHelperRectlinear.Fixed, self).__init__( \ + loc, nth_coord, + passingthrough_point, label_direction) + + self.axis = [axes.xaxis, axes.yaxis][self.nth_coord] + + + + # TICK + + def get_tick_iterators(self, axes): """tick_loc, tick_angle, tick_label""" angle = 0 - 90 * self.nth_coord + if self.passthru_pt[1 - self.nth_coord] > 0.5: + angle = 180+angle major = self.axis.major majorLocs = major.locator() @@ -338,11 +486,56 @@ minor.formatter.set_locs(minorLocs) minorLabels = [minor.formatter(val, i) for i, val in enumerate(minorLocs)] - tr2ax = self.transform + self.axes.transAxes.inverted() + trans_tick = self.get_tick_transform(axes) + tr2ax = trans_tick + axes.transAxes.inverted() + def _f(locs, labels): for x, l in zip(locs, labels): + c = list(self.passthru_pt) # copy + c[self.nth_coord] = x + + # check if the tick point is inside axes + c2 = tr2ax.transform_point(c) + delta=0.001 + if 0. -delta<= c2[self.nth_coord] <= 1.+delta: + yield c, angle, l + + return _f(majorLocs, majorLabels), _f(minorLocs, minorLabels) + + + + class Floating(AxisArtistHelper.Floating): + def __init__(self, axes, nth_coord, + passingthrough_point, label_direction, transform): + + super(AxisArtistHelperRectlinear.Floating, self).__init__( \ + nth_coord, passingthrough_point, label_direction, transform) + + self.axis = [axes.xaxis, axes.yaxis][self.nth_coord] + + + def get_tick_iterators(self, axes): + """tick_loc, tick_angle, tick_label""" + + angle = 0 - 90 * self.nth_coord + + major = self.axis.major + majorLocs = major.locator() + major.formatter.set_locs(majorLocs) + majorLabels = [major.formatter(val, i) for i, val in enumerate(majorLocs)] + + minor = self.axis.minor + minorLocs = minor.locator() + minor.formatter.set_locs(minorLocs) + minorLabels = [minor.formatter(val, i) for i, val in enumerate(minorLocs)] + + tr2ax = self.transform + axes.transAxes.inverted() + + def _f(locs, labels): + for x, l in zip(locs, labels): + c = list(self.passingthrough_point) # copy c[self.nth_coord] = x c1, c2 = tr2ax.transform_point(c) @@ -352,34 +545,60 @@ return _f(majorLocs, majorLabels), _f(minorLocs, minorLabels) -class GridHelperRectlinear(object): + +class GridHelperBase(object): + def __init__(self): - self.axes = None self._force_update = True + self._old_limits = None + super(GridHelperBase, self).__init__() - def set_axes(self, axes): - self.axes = axes + def update_lim(self, axes): + x1, x2 = axes.get_xlim() + y1, y2 = axes.get_ylim() + + if self._force_update or self._old_limits != (x1, x2, y1, y2): + self._update(x1, x2, y1, y2) + self._force_update = False + self._old_limits = (x1, x2, y1, y2) + + def _update(self, x1, x2, y1, y2): - self._force_update = False + pass + def invalidate(self): self._force_update = True + def get_gridlines(self): return [] - def _get_axisline_helper(self, nth_coord, loc, + + +class GridHelperRectlinear(GridHelperBase): + + + def __init__(self, axes): + + super(GridHelperRectlinear, self).__init__() + self.axes = axes + + #def set_axes(self, axes): + # self.axes = axes + + def _get_axisline_helper_deprecated(self, nth_coord, loc, passingthrough_point, transform=None): if transform is None or transform is self.axes.transAxes: - return AxisLineHelper.Fixed(self.axes, loc, + return AxisArtistHelper.Fixed(self.axes, loc, nth_coord, passingthrough_point) else: label_direction = loc - return AxisLineHelper.Floating(self.axes, + return AxisArtistHelper.Floating(self.axes, nth_coord, passingthrough_point, label_direction, transform) @@ -390,16 +609,23 @@ #transform=None, tick_direction="in", label_direction=None, - offset=None): + offset=None, + axes=None, + ): - _helper = AxisLineHelper.Fixed(self.axes, loc, - nth_coord, passthrough_point) + if axes is None: + warnings.warn("'new_fixed_axis' explicitly requires the axes keyword.") + axes = self.axes - axisline = AxisLine(self.axes, _helper, - #tick_direction="in", - offset=offset, - ) + _helper = AxisArtistHelperRectlinear.Fixed(axes, loc, + nth_coord, + passthrough_point) + axisline = AxisArtist(axes, _helper, + #tick_direction="in", + offset=offset, + ) + return axisline @@ -407,21 +633,27 @@ transform=None, tick_direction="in", label_direction=None, - ): + axes=None, + ): - _helper = AxisLineHelper.Floating(self.axes, - nth_coord, passthrough_point, - label_direction, - transform) + if axes is None: + warnings.warn("'new_floating_axis' explicitly requires the axes keyword.") + axes = self.axes - axisline = AxisLine(self.axes, _helper, - #tick_direction="in", - ) + _helper = AxisArtistHelperRectlinear.Floating( \ + axes, + nth_coord, passthrough_point, + label_direction, + transform) + axisline = AxisArtist(axes, _helper, + #tick_direction="in", + ) + return axisline - def new_axisline(self, loc, + def new_axisline_deprecated(self, loc, nth_coord=None, passthrough_point=None, transform=None, tick_direction="in", @@ -435,7 +667,7 @@ passthrough_point, transform) - axisline = AxisLine(self.axes, _helper, + axisline = AxisArtist(self.axes, _helper, #tick_direction="in", offset=offset, ) @@ -444,18 +676,9 @@ - - - -class XYEvent: - def __init__(self, xy): - self.x, self.y = xy - - from matplotlib.lines import Line2D class Ticks(Line2D): -#LineCollection def __init__(self, ticksize, **kwargs): self.ticksize = ticksize self.locs_angles = [] @@ -608,6 +831,9 @@ # return Bbox.from_bounds(0, 0, 0, 0) + + + class AxisLabel(mtext.Text): def __init__(self, *kl, **kwargs): self._axis = kwargs.pop("axis", None) @@ -635,279 +861,40 @@ return self._text -class AxisGridLineBase(martist.Artist): - def __init__(self, *kl, **kw): - super(AxisGridLineBase, self).__init__(*kl, **kw) +class GridlinesCollection(LineCollection): + def __init__(self, *kl, **kwargs): + super(GridlinesCollection, self).__init__(*kl, **kwargs) + self.set_grid_helper(None) + def set_grid_helper(self, grid_helper): + self._grid_helper = grid_helper - -class GridLine(AxisGridLineBase): - """ a line along which the n-th axes coord is constant.""" - - LABELPAD = 5 - ZORDER=2.5 - - def __init__(self, axes, - helper, - #offset_transform=None, - offset=None, - major_tick_size=None, - major_tick_pad=None, - minor_tick_size=None, - minor_tick_pad=None, - **kw): - - AxisGridLineBase.__init__(self, **kw) - - self.axes = axes - - self._helper = helper - - #if offset_transform is None: - # self.offset_transform = IdentityTransform() - #else: - # self.offset_transform = offset_transform - - if offset is None: - offset = (0, 0) - self.dpi_transform = Affine2D() - self.offset_transform = ScaledTranslation(offset[0], offset[1], - self.dpi_transform) - - self.set_transform(axes.transAxes + \ - self.offset_transform) - - self._label_visible = True - self._majortick_visible = True - self._majorticklabel_visible = True - self._minortick_visible = True - self._minorticklabel_visible = True - - - if self._helper.label_direction in ["left", "right"]: - axis_name = "ytick" - else: - axis_name = "xtick" - - - if major_tick_size is None: - self.major_tick_size = rcParams['%s.major.size'%axis_name] - if major_tick_pad is None: - self.major_tick_pad = rcParams['%s.major.pad'%axis_name] - if minor_tick_size is None: - self.minor_tick_size = rcParams['%s.minor.size'%axis_name] - if minor_tick_pad is None: - self.minor_tick_pad = rcParams['%s.minor.pad'%axis_name] - - self._init_line() - self._init_ticks() - self._init_label() - - self.set_zorder(self.ZORDER) - - def _init_line(self): - tran = self._helper.get_line_transform() + self.offset_transform - self.line = BezierPath(self._helper.get_line(), - color=rcParams['axes.edgecolor'], - linewidth=rcParams['axes.linewidth'], - transform=tran) - - def get_helper(self): - return self._helper - - def _draw_line(self, renderer): - self.line.set_path(self._helper.get_line()) - self.line.draw(renderer) - - - def _init_ticks(self): - - transform=self._helper.get_tick_transform()+self.offset_transform - - self.major_ticks = Ticks(self.major_tick_size, - transform=transform) - self.minor_ticks = Ticks(self.minor_tick_size, - transform=transform) - - - size = rcParams['xtick.labelsize'] - - fontprops = font_manager.FontProperties(size=size) - tvhl = self._helper.get_ticklabel_transform(self.major_tick_pad, - fontprops=fontprops, - renderer=None, - trans=transform) - trans, vert, horiz, label_a = tvhl - - color = rcParams['xtick.color'] - self.major_ticklabels = TickLabels(size, color=color) - self.minor_ticklabels = TickLabels(size, color=color) - - #self.major_ticklabels = TickLabels(size, axis=self.axis) - #self.minor_ticklabels = TickLabels(size, axis=self.axis) - - - self.major_ticklabels.set(figure = self.axes.figure, - rotation = label_a, - transform=trans, - va=vert, - ha=horiz, - fontproperties=fontprops) - - self.minor_ticklabels.set(figure = self.axes.figure, - rotation = label_a, - transform=trans, - va=vert, - ha=horiz, - fontproperties=fontprops) - - - def _draw_ticks(self, renderer): - #majortick_iter, minortick_iter): - #major_locs, major_angles, - #minor_locs, minor_angles): - - majortick_iter, minortick_iter = self._helper.get_tick_iterators() - - tick_loc_angles = [] - tick_loc_labels = [] - for tick_loc, tick_angle, tick_label in majortick_iter: - tick_loc_angles.append((tick_loc, tick_angle)) - tick_loc_labels.append((tick_loc, tick_label)) - - - transform=self._helper.get_tick_transform()+self.offset_transform - fontprops = font_manager.FontProperties(size=12) - tvhl = self._helper.get_ticklabel_transform(self.major_tick_pad, - fontprops=fontprops, - renderer=renderer, - trans=transform) - trans, va, ha, a = tvhl - self.major_ticklabels.set(transform=trans, - va=va, ha=ha, rotation=a) - - - self.major_ticks.update_locs_angles(tick_loc_angles, renderer) - self.major_ticklabels.update_locs_labels(tick_loc_labels, renderer) - - self.major_ticks.draw(renderer) - self.major_ticklabels.draw(renderer) - - tick_loc_angles = [] - tick_loc_labels = [] - for tick_loc, tick_angle, tick_label in minortick_iter: - tick_loc_angles.append((tick_loc, tick_angle)) - tick_loc_labels.append((tick_loc, tick_label)) - - self.minor_ticks.update_locs_angles(tick_loc_angles, renderer) - self.minor_ticklabels.update_locs_labels(tick_loc_labels, renderer) - - self.minor_ticks.draw(renderer) - self.minor_ticklabels.draw(renderer) - - return self.major_ticklabels.get_window_extents(renderer) - - def _init_label(self): - # x in axes coords, y in display coords (to be updated at draw - # time by _update_label_positions) - fontprops = font_manager.FontProperties(size=rcParams['axes.labelsize']) - textprops = dict(fontproperties = fontprops, - color = rcParams['axes.labelcolor'], - ) - - self.label = AxisLabel(0, 0, "", - fontproperties=fontprops, - color = rcParams['axes.labelcolor'], - ) - self.label.set_figure(self.axes.figure) - - #self._set_artist_props(label) - - def _draw_label(self, renderer, bboxes): - - if not self.label.get_visible(): - return - - fontprops = font_manager.FontProperties(size=rcParams['axes.labelsize']) - pad_points = self.LABELPAD + self.major_tick_pad - xy, tr = self._helper.get_label_pos() - if xy is None: return - - x, y = xy - tr2, va, ha, a = self._helper.get_label_transform(pad_points, fontprops, - renderer, - bboxes=bboxes, - trans=tr+self.offset_transform) - - self.label.set(x=x, y=y, - transform=tr2, - va=va, ha=ha, rotation=a) - -# if self.label.get_text() == "__from_axes__": -# label_text = self.axis.get_label().get_text() -# self.label.set_text(label_text) -# self.label.draw(renderer) -# self.label.set_text("__from_axes__") -# else: - - self.label.draw(renderer) - - - def set_label(self, s): - self.label.set_text(s) - - def draw(self, renderer): - 'Draw the axis lines, tick lines and labels' + if self._grid_helper is not None: + self._grid_helper.update_lim(self.axes) + #self.set_transform(self._grid_helper.get_gridlines_transform()) + gl = self._grid_helper.get_gridlines() + if gl: + self.set_segments([np.transpose(l) for l in gl]) + else: + self.set_segments([]) + super(GridlinesCollection, self).draw(renderer) - if not self.get_visible(): return - renderer.open_group(__name__) +class AxisGridLineBase(martist.Artist): + def __init__(self, *kl, **kw): + super(AxisGridLineBase, self).__init__(*kl, **kw) - dpi_cor = renderer.points_to_pixels(1.) - self.dpi_transform.clear().scale(dpi_cor, dpi_cor) +class AxisArtist(AxisGridLineBase): + """ + an artist which draws axis (a line along which the n-th axes coord + is constant) line, ticks, ticklabels, and axis label. - self._draw_line(renderer) - bboxes = self._draw_ticks(renderer) + It requires an AxisArtistHelper instance. + """ - #self._draw_offsetText(renderer) - self._draw_label(renderer, bboxes) - - renderer.close_group(__name__) - - def get_ticklabel_extents(self, renderer): - pass - - def toggle(self, all=None, ticks=None, ticklabels=None, label=None): - if all: - _ticks, _ticklabels, _label = True, True, True - elif all is not None: - _ticks, _ticklabels, _label = False, False, False - else: - _ticks, _ticklabels, _label = None, None, None - - if ticks is not None: - _ticks = ticks - if ticklabels is not None: - _ticklabels = ticklabels - if label is not None: - _label = label - - if _ticks is not None: - self.major_ticks.set_visible(_ticks) - self.minor_ticks.set_visible(_ticks) - if _ticklabels is not None: - self.major_ticklabels.set_visible(_ticklabels) - self.minor_ticklabels.set_visible(_ticklabels) - if _label is not None: - self.label.set_visible(_label) - - -class AxisLine(AxisGridLineBase): - """ a line along which the n-th axes coord is constant.""" - LABELPAD = 5 ZORDER=2.5 @@ -920,26 +907,23 @@ minor_tick_size=None, minor_tick_pad=None, **kw): - + """ + axes is also used to follow the axis attribute (tick color, etc). + """ AxisGridLineBase.__init__(self, **kw) self.axes = axes - self._helper = helper + self._axis_artist_helper = helper - #if offset_transform is None: - # self.offset_transform = IdentityTransform() - #else: - # self.offset_transform = offset_transform - if offset is None: offset = (0, 0) self.dpi_transform = Affine2D() self.offset_transform = ScaledTranslation(offset[0], offset[1], self.dpi_transform) - self.set_transform(axes.transAxes + \ - self.offset_transform) + #self.set_transform(axes.transAxes + \ + # self.offset_transform) self._label_visible = True self._majortick_visible = True @@ -948,7 +932,7 @@ self._minorticklabel_visible = True - if self._helper.label_direction in ["left", "right"]: + if self._axis_artist_helper.label_direction in ["left", "right"]: axis_name = "ytick" self.axis = axes.yaxis else: @@ -967,29 +951,35 @@ self._init_line() self._init_ticks() - self._init_offsetText(self._helper.label_direction) + self._init_offsetText(self._axis_artist_helper.label_direction) self._init_label() self.set_zorder(self.ZORDER) + def get_transform(self): + return self.axes.transAxes + self.offset_transform + + def get_helper(self): + return self._axis_artist_helper + + def _init_line(self): - tran = self._helper.get_line_transform() + self.offset_transform - self.line = BezierPath(self._helper.get_line(), + tran = self._axis_artist_helper.get_line_transform(self.axes) \ + + self.offset_transform + self.line = BezierPath(self._axis_artist_helper.get_line(self.axes), color=rcParams['axes.edgecolor'], linewidth=rcParams['axes.linewidth'], transform=tran) - def get_helper(self): - return self._helper - def _draw_line(self, renderer): - self.line.set_path(self._helper.get_line()) + self.line.set_path(self._axis_artist_helper.get_line(self.axes)) self.line.draw(renderer) def _init_ticks(self): - transform=self._helper.get_tick_transform()+self.offset_transform + transform=self._axis_artist_helper.get_tick_transform(self.axes) \ + + self.offset_transform self.major_ticks = Ticks(self.major_tick_size, axis=self.axis, @@ -1002,16 +992,17 @@ size = rcParams['xtick.labelsize'] fontprops = font_manager.FontProperties(size=size) - tvhl = self._helper.get_ticklabel_transform(self.major_tick_pad, - fontprops=fontprops, - renderer=None, - trans=transform) + #tvhl = self._axis_artist_helper.get_ticklabel_transform( + tvhl = self._axis_artist_helper.get_ticklabel_offset_transform( \ + self.axes, + self.major_tick_pad, + fontprops=fontprops, + renderer=None, + ) + #trans=transform) trans, vert, horiz, label_a = tvhl + trans = transform + trans - #color = rcParams['xtick.color'] - #self.major_ticklabels = TickLabels(size, color=color) - #self.minor_ticklabels = TickLabels(size, color=color) - self.major_ticklabels = TickLabels(size, axis=self.axis) self.minor_ticklabels = TickLabels(size, axis=self.axis) @@ -1040,7 +1031,7 @@ x,y,va,ha = self._offsetText_pos[direction] - #d = self._helper.label_direction + #d = self._axis_artist_helper.label_direction #fp = font_manager.FontProperties(size=rcParams['xtick.labelsize']) #fp = font_manager.FontProperties(size=self.major_ticklabels.get_size()) self.offsetText = mtext.Annotation("", @@ -1056,7 +1047,7 @@ def _update_offsetText(self): - self.offsetText.set_text( self._helper.axis.major.formatter.get_offset() ) + self.offsetText.set_text( self.axis.major.formatter.get_offset() ) self.offsetText.set_size(self.major_ticklabels.get_size()) offset = self.major_tick_pad + self.major_ticklabels.get_size() + 2. self.offsetText.xytext= (0, offset) @@ -1072,7 +1063,8 @@ #major_locs, major_angles, #minor_locs, minor_angles): - majortick_iter, minortick_iter = self._helper.get_tick_iterators() + majortick_iter, minortick_iter = \ + self._axis_artist_helper.get_tick_iterators(self.axes) tick_loc_angles = [] tick_loc_labels = [] @@ -1081,13 +1073,19 @@ tick_loc_labels.append((tick_loc, tick_label)) - transform=self._helper.get_tick_transform()+self.offset_transform + transform=self._axis_artist_helper.get_tick_transform(self.axes) \ + + self.offset_transform fontprops = font_manager.FontProperties(size=12) - tvhl = self._helper.get_ticklabel_transform(self.major_tick_pad, - fontprops=fontprops, - renderer=renderer, - trans=transform) + tvhl = self._axis_artist_helper.get_ticklabel_offset_transform( \ + self.axes, + self.major_tick_pad, + fontprops=fontprops, + renderer=renderer, + ) + #trans=transform) trans, va, ha, a = tvhl + trans = transform + trans + self.major_ticklabels.set(transform=trans, va=va, ha=ha, rotation=a) @@ -1140,14 +1138,18 @@ fontprops = font_manager.FontProperties(size=rcParams['axes.labelsize']) pad_points = self.LABELPAD + self.major_tick_pad - xy, tr = self._helper.get_label_pos() + xy, tr = self._axis_artist_helper.get_label_pos(self.axes) if xy is None: return x, y = xy - tr2, va, ha, a = self._helper.get_label_transform(pad_points, fontprops, - renderer, - bboxes=bboxes, - trans=tr+self.offset_transform) + tr2, va, ha, a = self._axis_artist_helper.get_label_offset_transform(\ + self.axes, + pad_points, fontprops, + renderer, + bboxes=bboxes, + ) + #trans=tr+self.offset_transform) + tr2 = (tr+self.offset_transform) + tr2 self.label.set(x=x, y=y, transform=tr2, @@ -1174,6 +1... [truncated message content] |