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

Reply via email to