Revision: 8048 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=8048&view=rev Author: astraw Date: 2009-12-21 03:49:59 +0000 (Mon, 21 Dec 2009)
Log Message: ----------- spines and ticks: implement smart bounds Modified Paths: -------------- trunk/matplotlib/examples/pylab_examples/spine_placement_demo.py trunk/matplotlib/lib/matplotlib/axis.py trunk/matplotlib/lib/matplotlib/spines.py Modified: trunk/matplotlib/examples/pylab_examples/spine_placement_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/spine_placement_demo.py 2009-12-21 02:24:14 UTC (rev 8047) +++ trunk/matplotlib/examples/pylab_examples/spine_placement_demo.py 2009-12-21 03:49:59 UTC (rev 8048) @@ -37,6 +37,8 @@ ax.spines['right'].set_color('none') ax.spines['bottom'].set_position('center') ax.spines['top'].set_color('none') +ax.spines['left'].set_smart_bounds(True) +ax.spines['bottom'].set_smart_bounds(True) ax.xaxis.set_ticks_position('bottom') ax.yaxis.set_ticks_position('left') @@ -47,6 +49,8 @@ ax.spines['right'].set_color('none') ax.spines['bottom'].set_position('zero') ax.spines['top'].set_color('none') +ax.spines['left'].set_smart_bounds(True) +ax.spines['bottom'].set_smart_bounds(True) ax.xaxis.set_ticks_position('bottom') ax.yaxis.set_ticks_position('left') @@ -57,6 +61,8 @@ ax.spines['right'].set_color('none') ax.spines['bottom'].set_position(('axes',0.1)) ax.spines['top'].set_color('none') +ax.spines['left'].set_smart_bounds(True) +ax.spines['bottom'].set_smart_bounds(True) ax.xaxis.set_ticks_position('bottom') ax.yaxis.set_ticks_position('left') @@ -67,15 +73,17 @@ ax.spines['right'].set_color('none') ax.spines['bottom'].set_position(('data',2)) ax.spines['top'].set_color('none') +ax.spines['left'].set_smart_bounds(True) +ax.spines['bottom'].set_smart_bounds(True) ax.xaxis.set_ticks_position('bottom') ax.yaxis.set_ticks_position('left') - # ---------------------------------------------------- def adjust_spines(ax,spines): for loc, spine in ax.spines.iteritems(): if loc in spines: spine.set_position(('outward',10)) # outward by 10 points + spine.set_smart_bounds(True) else: spine.set_color('none') # don't draw spine Modified: trunk/matplotlib/lib/matplotlib/axis.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axis.py 2009-12-21 02:24:14 UTC (rev 8047) +++ trunk/matplotlib/lib/matplotlib/axis.py 2009-12-21 03:49:59 UTC (rev 8048) @@ -15,6 +15,7 @@ import matplotlib.ticker as mticker import matplotlib.transforms as mtransforms import matplotlib.units as munits +import numpy as np GRIDLINE_INTERPOLATION_STEPS = 180 @@ -539,6 +540,8 @@ #self.minor = dummy() self._autolabelpos = True + self._smart_bounds = False + self.label = self._get_label() self.labelpad = 5 self.offsetText = self._get_offset_text() @@ -737,6 +740,14 @@ bbox2 = mtransforms.Bbox.from_extents(0, 0, 0, 0) return bbox, bbox2 + def set_smart_bounds(self,value): + """set the axis to have smart bounds""" + self._smart_bounds = value + + def get_smart_bounds(self): + """get whether the axis has smart bounds""" + return self._smart_bounds + @allow_rasterization def draw(self, renderer, *args, **kwargs): 'Draw the axis lines, grid lines, tick lines and labels' @@ -746,7 +757,47 @@ if not self.get_visible(): return renderer.open_group(__name__) interval = self.get_view_interval() - for tick, loc, label in self.iter_ticks(): + tick_tups = [ t for t in self.iter_ticks()] + if self._smart_bounds: + # handle inverted limits + view_low, view_high = min(*interval), max(*interval) + data_low, data_high = self.get_data_interval() + if data_low > data_high: + data_low, data_high = data_high, data_low + locs = [ti[1] for ti in tick_tups] + locs.sort() + locs = np.array(locs) + if len(locs): + if data_low <= view_low: + # data extends beyond view, take view as limit + ilow = view_low + else: + # data stops within view, take best tick + cond = locs <= data_low + good_locs = locs[cond] + if len(good_locs) > 0: + # last tick prior or equal to first data point + ilow = good_locs[-1] + else: + # No ticks (why not?), take first tick + ilow = locs[0] + if data_high >= view_high: + # data extends beyond view, take view as limit + ihigh = view_high + else: + # data stops within view, take best tick + cond = locs >= data_high + good_locs = locs[cond] + if len(good_locs) > 0: + # first tick after or equal to last data point + ihigh = good_locs[0] + else: + # No ticks (why not?), take last tick + ihigh = locs[-1] + tick_tups = [ ti for ti in tick_tups + if (ti[1] >= ilow) and (ti[1] <= ihigh)] + + for tick, loc, label in tick_tups: if tick is None: continue if not mtransforms.interval_contains(interval, loc): continue tick.update_position(loc) Modified: trunk/matplotlib/lib/matplotlib/spines.py =================================================================== --- trunk/matplotlib/lib/matplotlib/spines.py 2009-12-21 02:24:14 UTC (rev 8047) +++ trunk/matplotlib/lib/matplotlib/spines.py 2009-12-21 03:49:59 UTC (rev 8048) @@ -59,6 +59,7 @@ self.set_transform(self.axes.transData) # default transform self._bounds = None # default bounds + self._smart_bounds = False # Defer initial position determination. (Not much support for # non-rectangular axes is currently implemented, and this lets @@ -78,6 +79,20 @@ # Note: This cannot be calculated until this is added to an Axes self._patch_transform = mtransforms.IdentityTransform() + def set_smart_bounds(self,value): + """set the spine and associated axis to have smart bounds""" + self._smart_bounds = value + + # also set the axis if possible + if self.spine_type in ('left','right'): + self.axes.yaxis.set_smart_bounds(value) + elif self.spine_type in ('top','bottom'): + self.axes.xaxis.set_smart_bounds(value) + + def get_smart_bounds(self): + """get whether the spine has smart bounds""" + return self._smart_bounds + def set_patch_circle(self,center,radius): """set the spine to be circular""" self._patch_type = 'circle' @@ -141,6 +156,26 @@ if self.axis is not None: self.axis.cla() + def is_frame_like(self): + """return True if directly on axes frame + + This is useful for determining if a spine is the edge of an + old style MPL plot. If so, this function will return True. + """ + self._ensure_position_is_set() + position = self._position + if cbook.is_string_like(position): + if position=='center': + position = ('axes',0.5) + elif position=='zero': + position = ('data',0) + assert len(position)==2, "position should be 2-tuple" + position_type, amount = position + if position_type=='outward' and amount == 0: + return True + else: + return False + def _adjust_location(self): """automatically set spine bounds to the view interval""" @@ -154,6 +189,61 @@ low,high = self.axes.viewLim.intervalx else: raise ValueError('unknown spine spine_type: %s'%self.spine_type) + + if self._smart_bounds: + # attempt to set bounds in sophisticated way + if low > high: + # handle inverted limits + low,high=high,low + + viewlim_low = low + viewlim_high = high + + del low, high + + if self.spine_type in ('left','right'): + datalim_low,datalim_high = self.axes.dataLim.intervaly + ticks = self.axes.get_yticks() + elif self.spine_type in ('top','bottom'): + datalim_low,datalim_high = self.axes.dataLim.intervalx + ticks = self.axes.get_xticks() + # handle inverted limits + ticks = list(ticks) + ticks.sort() + ticks = np.array(ticks) + if datalim_low > datalim_high: + datalim_low, datalim_high = datalim_high, datalim_low + + if datalim_low < viewlim_low: + # Data extends past view. Clip line to view. + low = viewlim_low + else: + # Data ends before view ends. + cond = (ticks <= datalim_low) & (ticks >= viewlim_low) + tickvals = ticks[cond] + if len(tickvals): + # A tick is less than or equal to lowest data point. + low = tickvals[-1] + else: + # No tick is available + low = datalim_low + low = max(low,viewlim_low) + + if datalim_high > viewlim_high: + # Data extends past view. Clip line to view. + high = viewlim_high + else: + # Data ends before view ends. + cond = (ticks >= datalim_high) & (ticks <= viewlim_high) + tickvals = ticks[cond] + if len(tickvals): + # A tick is greater than or equal to highest data point. + high = tickvals[0] + else: + # No tick is available + high = datalim_high + high = min(high,viewlim_high) + else: low,high = self._bounds @@ -316,11 +406,16 @@ raise ValueError("unknown spine_transform type: %s"%what) def set_bounds( self, low, high ): + """Set the bounds of the spine.""" if self.spine_type == 'circle': raise ValueError( 'set_bounds() method incompatible with circular spines') self._bounds = (low, high) + def get_bounds( self ): + """Get the bounds of the spine.""" + return self._bounds + @classmethod def linear_spine(cls, axes, spine_type, **kwargs): """ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------------ This SF.Net email is sponsored by the Verizon Developer Community Take advantage of Verizon's best-in-class app development support A streamlined, 14 day to market process makes app distribution fast and easy Join now and get one step closer to millions of Verizon customers http://p.sf.net/sfu/verizon-dev2dev _______________________________________________ Matplotlib-checkins mailing list Matplotlib-checkins@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins