23
23
from matplotlib import mlab
24
24
from matplotlib import cm
25
25
from matplotlib import patches as mpatches
26
+ from matplotlib import path as mpath
26
27
from matplotlib import pbox as mpbox
27
28
from matplotlib import quiver as mquiver
28
29
from matplotlib import scale as mscale
@@ -1461,7 +1462,7 @@ def set_axis_bgcolor(self, color):
1461
1462
self .axesPatch .set_facecolor (color )
1462
1463
1463
1464
### data limits, ticks, tick labels, and formatting
1464
-
1465
+
1465
1466
def get_xlim (self ):
1466
1467
'Get the x axis range [xmin, xmax]'
1467
1468
return self .viewLim .intervalx
@@ -1503,11 +1504,6 @@ def set_xlim(self, xmin=None, xmax=None, emit=True, **kwargs):
1503
1504
if xmin is None :xmin = old_xmin
1504
1505
if xmax is None :xmax = old_xmax
1505
1506
1506
- # MGDTODO
1507
- # if (self.transData.get_funcx().get_type()==mtrans.LOG10
1508
- # and min(xmin, xmax)<=0):
1509
- # raise ValueError('Cannot set nonpositive limits with log transform')
1510
-
1511
1507
xmin ,xmax = mtransforms .nonsingular (xmin ,xmax ,increasing = False )
1512
1508
1513
1509
self .viewLim .intervalx = (xmin ,xmax )
@@ -1516,7 +1512,7 @@ def set_xlim(self, xmin=None, xmax=None, emit=True, **kwargs):
1516
1512
# Call all of the other x-axes that are shared with this one
1517
1513
for other in self ._shared_x_axes .get_siblings (self ):
1518
1514
if other is not self :
1519
- other .set_xlim (self .viewLim .xmin , self . viewLim . xmax ,emit = False )
1515
+ other .set_xlim (self .viewLim .intervalx ,emit = False )
1520
1516
1521
1517
return xmin ,xmax
1522
1518
@@ -1634,7 +1630,7 @@ def get_yscale(self):
1634
1630
'return the yaxis scale string: log or linear'
1635
1631
return self .yaxis .get_scale ()
1636
1632
1637
- def set_yscale (self ,value ,basey = 10 , subsy = None ):
1633
+ def set_yscale (self ,value ,** kwargs ):
1638
1634
"""
1639
1635
SET_YSCALE(value, basey=10, subsy=None)
1640
1636
@@ -1652,7 +1648,7 @@ def set_yscale(self, value, basey=10, subsy=None):
1652
1648
1653
1649
ACCEPTS: ['log' | 'linear']
1654
1650
"""
1655
- self .yaxis .set_scale (value ,basey , subsy )
1651
+ self .yaxis .set_scale (value ,** kwargs )
1656
1652
self ._update_transScale ()
1657
1653
1658
1654
def get_yticks (self ):
@@ -1788,6 +1784,32 @@ def set_navigate_mode(self, b):
1788
1784
"""
1789
1785
self ._navigate_mode = b
1790
1786
1787
+ def drag_pan (self ,button ,x ,y ,startx ,starty ,start_lim ,start_trans ):
1788
+ if button == 1 :
1789
+ inverse = start_trans .inverted ()
1790
+ dx = startx - x
1791
+ dy = starty - y
1792
+ result = self .bbox .frozen ().translated (dx ,dy ).transformed (inverse )
1793
+ elif button == 3 :
1794
+ try :
1795
+ inverse = start_trans .inverted ()
1796
+ dx = (startx - x )/ float (self .bbox .width )
1797
+ dy = (starty - y )/ float (self .bbox .height )
1798
+ xmin ,ymin ,xmax ,ymax = start_lim .lbrt
1799
+
1800
+ alpha = npy .power (10.0 , (dx ,dy ))
1801
+ start = inverse .transform_point ((startx ,starty ))
1802
+ lim_points = start_lim .get_points ()
1803
+ result = start + alpha * (lim_points - start )
1804
+ result = mtransforms .Bbox (result )
1805
+ except OverflowError :
1806
+ warnings .warn ('Overflow while panning' )
1807
+ return
1808
+
1809
+ # MGDTODO: Could we do this with a single set_lim?
1810
+ self .set_xlim (* result .intervalx )
1811
+ self .set_ylim (* result .intervaly )
1812
+
1791
1813
def get_cursor_props (self ):
1792
1814
"""return the cursor props as a linewidth, color tuple where
1793
1815
linewidth is a float and color is an RGBA tuple"""
@@ -5658,8 +5680,253 @@ def __init__(self, fig, *args, **kwargs):
5658
5680
self ,fig ,
5659
5681
[self .figLeft ,self .figBottom ,self .figW ,self .figH ],** kwargs )
5660
5682
5683
+ ######################################################################
5684
+ # New Polar Axes
5685
+
5686
+ class PolarAxes (Axes ):
5687
+ class PolarTransform (mtransforms .Transform ):
5688
+ input_dims = 2
5689
+ output_dims = 2
5690
+ is_separable = False
5691
+
5692
+ def transform (self ,tr ):
5693
+ xy = npy .zeros (tr .shape ,npy .float_ )
5694
+ t = tr [:,0 :1 ]
5695
+ r = tr [:,1 :2 ]
5696
+ x = xy [:,0 :1 ]
5697
+ y = xy [:,1 :2 ]
5698
+ x += r * npy .cos (t )
5699
+ y += r * npy .sin (t )
5700
+ return xy
5701
+ transform_non_affine = transform
5702
+
5703
+ def interpolate (self ,a ,steps ):
5704
+ steps = npy .floor (steps )
5705
+ new_length = ((len (a )- 1 )* steps )+ 1
5706
+ new_shape = list (a .shape )
5707
+ new_shape [0 ]= new_length
5708
+ result = npy .zeros (new_shape ,a .dtype )
5709
+
5710
+ result [0 ]= a [0 ]
5711
+ a0 = a [0 :- 1 ]
5712
+ a1 = a [1 : ]
5713
+ delta = ((a1 - a0 )/ steps )
5714
+
5715
+ for i in range (1 ,int (steps )+ 1 ):
5716
+ result [i ::steps ]= delta * i + a0
5717
+
5718
+ return result
5719
+
5720
+ # def transform_path(self, path):
5721
+ # twopi = 2.0 * npy.pi
5722
+ # halfpi = 0.5 * npy.pi
5723
+
5724
+ # vertices = path.vertices
5725
+ # t0 = vertices[0:-1, 0]
5726
+ # t1 = vertices[1: , 0]
5727
+ # td = npy.where(t1 > t0, t1 - t0, twopi - (t0 - t1))
5728
+ # maxtd = td.max()
5729
+ # interpolate = npy.ceil(maxtd / halfpi)
5730
+ # if interpolate > 1.0:
5731
+ # vertices = self.interpolate(vertices, interpolate)
5732
+
5733
+ # vertices = self.transform(vertices)
5734
+
5735
+ # result = npy.zeros((len(vertices) * 3 - 2, 2), npy.float_)
5736
+ # codes = mpath.Path.CURVE4 * npy.ones((len(vertices) * 3 - 2, ), mpath.Path.code_type)
5737
+ # result[0] = vertices[0]
5738
+ # codes[0] = mpath.Path.MOVETO
5739
+
5740
+ # kappa = 4.0 * ((npy.sqrt(2.0) - 1.0) / 3.0)
5741
+ # kappa = 0.5
5742
+
5743
+ # p0 = vertices[0:-1]
5744
+ # p1 = vertices[1: ]
5745
+
5746
+ # x0 = p0[:, 0:1]
5747
+ # y0 = p0[:, 1: ]
5748
+ # b0 = ((y0 - x0) - y0) / ((x0 + y0) - x0)
5749
+ # a0 = y0 - b0*x0
5750
+
5751
+ # x1 = p1[:, 0:1]
5752
+ # y1 = p1[:, 1: ]
5753
+ # b1 = ((y1 - x1) - y1) / ((x1 + y1) - x1)
5754
+ # a1 = y1 - b1*x1
5755
+
5756
+ # x = -(a0-a1) / (b0-b1)
5757
+ # y = a0 + b0*x
5758
+
5759
+ # xk = (x - x0) * kappa + x0
5760
+ # yk = (y - y0) * kappa + y0
5761
+
5762
+ # result[1::3, 0:1] = xk
5763
+ # result[1::3, 1: ] = yk
5764
+
5765
+ # xk = (x - x1) * kappa + x1
5766
+ # yk = (y - y1) * kappa + y1
5767
+
5768
+ # result[2::3, 0:1] = xk
5769
+ # result[2::3, 1: ] = yk
5770
+
5771
+ # result[3::3] = p1
5772
+
5773
+ # print vertices[-2:]
5774
+ # print result[-2:]
5775
+
5776
+ # return mpath.Path(result, codes)
5777
+
5778
+ # twopi = 2.0 * npy.pi
5779
+ # halfpi = 0.5 * npy.pi
5780
+
5781
+ # vertices = path.vertices
5782
+ # t0 = vertices[0:-1, 0]
5783
+ # t1 = vertices[1: , 0]
5784
+ # td = npy.where(t1 > t0, t1 - t0, twopi - (t0 - t1))
5785
+ # maxtd = td.max()
5786
+ # interpolate = npy.ceil(maxtd / halfpi)
5787
+
5788
+ # print "interpolate", interpolate
5789
+ # if interpolate > 1.0:
5790
+ # vertices = self.interpolate(vertices, interpolate)
5791
+
5792
+ # result = npy.zeros((len(vertices) * 3 - 2, 2), npy.float_)
5793
+ # codes = mpath.Path.CURVE4 * npy.ones((len(vertices) * 3 - 2, ), mpath.Path.code_type)
5794
+ # result[0] = vertices[0]
5795
+ # codes[0] = mpath.Path.MOVETO
5796
+
5797
+ # kappa = 4.0 * ((npy.sqrt(2.0) - 1.0) / 3.0)
5798
+ # tkappa = npy.arctan(kappa)
5799
+ # hyp_kappa = npy.sqrt(kappa*kappa + 1.0)
5800
+
5801
+ # t0 = vertices[0:-1, 0]
5802
+ # t1 = vertices[1: , 0]
5803
+ # r0 = vertices[0:-1, 1]
5804
+ # r1 = vertices[1: , 1]
5805
+
5806
+ # td = npy.where(t1 > t0, t1 - t0, twopi - (t0 - t1))
5807
+ # td_scaled = td / (npy.pi * 0.5)
5808
+ # rd = r1 - r0
5809
+ # r0kappa = r0 * kappa * td_scaled
5810
+ # r1kappa = r1 * kappa * td_scaled
5811
+ # ravg_kappa = ((r1 + r0) / 2.0) * kappa * td_scaled
5812
+
5813
+ # result[1::3, 0] = t0 + (tkappa * td_scaled)
5814
+ # result[1::3, 1] = r0*hyp_kappa
5815
+ # # result[1::3, 1] = r0 / npy.cos(tkappa * td_scaled) # npy.sqrt(r0*r0 + ravg_kappa*ravg_kappa)
5816
+
5817
+ # result[2::3, 0] = t1 - (tkappa * td_scaled)
5818
+ # result[2::3, 1] = r1*hyp_kappa
5819
+ # # result[2::3, 1] = r1 / npy.cos(tkappa * td_scaled) # npy.sqrt(r1*r1 + ravg_kappa*ravg_kappa)
5820
+
5821
+ # result[3::3, 0] = t1
5822
+ # result[3::3, 1] = r1
5823
+
5824
+ # print vertices[:6], result[:6], t0[:6], t1[:6], td[:6], td_scaled[:6], tkappa
5825
+ # result = self.transform(result)
5826
+ # return mpath.Path(result, codes)
5827
+ # transform_path_non_affine = transform_path
5828
+
5829
+ def inverted (self ):
5830
+ return PolarAxes .InvertedPolarTransform ()
5831
+
5832
+ class PolarAffine (mtransforms .Affine2DBase ):
5833
+ def __init__ (self ,limits ):
5834
+ mtransforms .Affine2DBase .__init__ (self )
5835
+ self ._limits = limits
5836
+ self .set_children (limits )
5837
+ self ._mtx = None
5838
+
5839
+ def get_matrix (self ):
5840
+ if self ._invalid :
5841
+ xmin ,ymin ,xmax ,ymax = self ._limits .lbrt
5842
+ affine = mtransforms .Affine2D ().rotate (xmin ).scale (0.5 / ymax ).translate (0.5 ,0.5 )
5843
+ self ._mtx = affine .get_matrix ()
5844
+ self ._inverted = None
5845
+ self ._invalid = 0
5846
+ return self ._mtx
5847
+
5848
+ class InvertedPolarTransform (mtransforms .Transform ):
5849
+ input_dims = 2
5850
+ output_dims = 2
5851
+ is_separable = False
5852
+
5853
+ def transform (self ,xy ):
5854
+ x = xy [:,0 :1 ]
5855
+ y = xy [:,1 :]
5856
+ r = npy .sqrt (x * x + y * y )
5857
+ theta = npy .arccos (x / r )
5858
+ theta = npy .where (y < 0 ,2 * npy .pi - theta ,theta )
5859
+ return npy .concatenate ((theta ,r ),1 )
5860
+
5861
+ def inverted (self ):
5862
+ return PolarAxes .PolarTransform ()
5863
+
5864
+ def _set_lim_and_transforms (self ):
5865
+ """
5866
+ set the dataLim and viewLim BBox attributes and the
5867
+ transData and transAxes Transformation attributes
5868
+ """
5869
+ self .dataLim = mtransforms .Bbox .unit ()
5870
+ self .viewLim = mtransforms .Bbox .unit ()
5871
+ self .transAxes = mtransforms .BboxTransform (
5872
+ mtransforms .Bbox .unit (),self .bbox )
5661
5873
5874
+ # Transforms the x and y axis separately by a scale factor
5875
+ # It is assumed that this part will have non-linear components
5876
+ self .transScale = mtransforms .TransformWrapper (mtransforms .IdentityTransform ())
5877
+
5878
+ # A (possibly non-linear) projection on the (already scaled) data
5879
+ self .transProjection = self .PolarTransform ()
5662
5880
5881
+ # An affine transformation on the data, generally to limit the
5882
+ # range of the axes
5883
+ self .transProjectionAffine = self .PolarAffine (self .viewLim )
5884
+
5885
+ self .transData = self .transScale + self .transProjection + \
5886
+ self .transProjectionAffine + self .transAxes
5887
+
5888
+ def drag_pan (self ,button ,x ,y ,startx ,starty ,start_lim ,start_trans ):
5889
+ if button == 1 :
5890
+ inverse = start_trans .inverted ()
5891
+ startt ,startr = inverse .transform_point ((startx ,starty ))
5892
+ t ,r = inverse .transform_point ((x ,y ))
5893
+
5894
+ scale = r / startr
5895
+ self .set_ylim (start_lim .ymin ,start_lim .ymax / scale )
5896
+
5897
+ dt0 = t - startt
5898
+ dt1 = startt - t
5899
+ if abs (dt1 )< abs (dt0 ):
5900
+ dt = abs (dt1 )* sign (dt0 )* - 1.0
5901
+ else :
5902
+ dt = dt0 * - 1.0
5903
+ self .set_xlim (start_lim .xmin - dt ,start_lim .xmin - dt + npy .pi * 2.0 )
5904
+
5905
+ def set_rmax (self ,rmax ):
5906
+ self .viewLim .maxy = rmax
5907
+
5908
+ class PolarSubplot (SubplotBase ,PolarAxes ):
5909
+ """
5910
+ Create a polar subplot with
5911
+
5912
+ PolarSubplot(numRows, numCols, plotNum)
5913
+
5914
+ where plotNum=1 is the first plot number and increasing plotNums
5915
+ fill rows first. max(plotNum)==numRows*numCols
5916
+
5917
+ You can leave out the commas if numRows<=numCols<=plotNum<10, as
5918
+ in
5919
+
5920
+ Subplot(211) # 2 rows, 1 column, first (upper) plot
5921
+ """
5922
+ def __str__ (self ):
5923
+ return "PolarSubplot(%gx%g)" % (self .figW ,self .figH )
5924
+ def __init__ (self ,fig ,* args ,** kwargs ):
5925
+ SubplotBase .__init__ (self ,fig ,* args )
5926
+ PolarAxes .__init__ (
5927
+ self ,fig ,
5928
+ [self .figLeft ,self .figBottom ,self .figW ,self .figH ],** kwargs )
5929
+
5663
5930
martist .kwdocd ['Axes' ]= martist .kwdocd ['Subplot' ]= martist .kwdoc (Axes )
5664
5931
"""
5665
5932
# this is some discarded code I was using to find the minimum positive