|
From: <as...@us...> - 2009-12-21 03:50:07
|
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.
|