@@ -36,22 +36,33 @@ class PolarTransform(Transform):
3636 output_dims = 2
3737 is_separable = False
3838
39- def __init__ (self , axis = None ):
39+ def __init__ (self , axis = None , use_rmin = True ):
4040 Transform .__init__ (self )
4141 self ._axis = axis
42+ self ._use_rmin = use_rmin
4243
4344 def transform (self , tr ):
4445 xy = np .empty (tr .shape , np .float_ )
4546 if self ._axis is not None :
46- rmin = self ._axis .viewLim .ymin
47+ if self ._use_rmin :
48+ rmin = self ._axis .viewLim .ymin
49+ else :
50+ rmin = 0
51+ theta_offset = self ._axis .get_theta_offset ()
52+ theta_direction = self ._axis .get_theta_direction ()
4753 else :
4854 rmin = 0
55+ theta_offset = 0
56+ theta_direction = 1
4957
5058 t = tr [:, 0 :1 ]
5159 r = tr [:, 1 :2 ]
5260 x = xy [:, 0 :1 ]
5361 y = xy [:, 1 :2 ]
5462
63+ t *= theta_direction
64+ t += theta_offset
65+
5566 if rmin != 0 :
5667 r = r - rmin
5768 mask = r < 0
@@ -79,7 +90,7 @@ def transform_path(self, path):
7990 transform_path_non_affine .__doc__ = Transform .transform_path_non_affine .__doc__
8091
8192 def inverted (self ):
82- return PolarAxes .InvertedPolarTransform (self ._axis )
93+ return PolarAxes .InvertedPolarTransform (self ._axis , self . _use_rmin )
8394 inverted .__doc__ = Transform .inverted .__doc__
8495
8596 class PolarAffine (Affine2DBase ):
@@ -121,23 +132,40 @@ class InvertedPolarTransform(Transform):
121132 output_dims = 2
122133 is_separable = False
123134
124- def __init__ (self , axis = None ):
135+ def __init__ (self , axis = None , use_rmin = True ):
125136 Transform .__init__ (self )
126137 self ._axis = axis
138+ self ._use_rmin = use_rmin
127139
128140 def transform (self , xy ):
141+ if self ._axis is not None :
142+ if self ._use_rmin :
143+ rmin = self ._axis .viewLim .ymin
144+ else :
145+ rmin = 0
146+ theta_offset = self ._axis .get_theta_offset ()
147+ theta_direction = self ._axis .get_theta_direction ()
148+ else :
149+ rmin = 0
150+ theta_offset = 0
151+ theta_direction = 1
152+
129153 x = xy [:, 0 :1 ]
130154 y = xy [:, 1 :]
131155 r = np .sqrt (x * x + y * y )
132- if self ._axis is not None :
133- r += self ._axis .viewLim .ymin
134156 theta = np .arccos (x / r )
135157 theta = np .where (y < 0 , 2 * np .pi - theta , theta )
158+
159+ theta -= theta_offset
160+ theta *= theta_direction
161+
162+ r += rmin
163+
136164 return np .concatenate ((theta , r ), 1 )
137165 transform .__doc__ = Transform .transform .__doc__
138166
139167 def inverted (self ):
140- return PolarAxes .PolarTransform ()
168+ return PolarAxes .PolarTransform (self . _axis , self . _use_rmin )
141169 inverted .__doc__ = Transform .inverted .__doc__
142170
143171 class ThetaFormatter (Formatter ):
@@ -231,6 +259,9 @@ def cla(self):
231259 # Why do we need to turn on yaxis tick labels, but
232260 # xaxis tick labels are already on?
233261
262+ self .set_theta_offset (0 )
263+ self .set_theta_direction (1 )
264+
234265 def _init_axis (self ):
235266 "move this out of __init__ because non-separable axes don't use it"
236267 self .xaxis = maxis .XAxis (self )
@@ -253,7 +284,7 @@ def _set_lim_and_transforms(self):
253284 self .transProjection = self .PolarTransform (self )
254285
255286 # This one is not aware of rmin
256- self .transPureProjection = self .PolarTransform ()
287+ self .transPureProjection = self .PolarTransform (self , use_rmin = False )
257288
258289 # An affine transformation on the data, generally to limit the
259290 # range of the axes
@@ -346,6 +377,67 @@ def set_rmin(self, rmin):
346377 def get_rmin (self ):
347378 return self .viewLim .ymin
348379
380+ def set_theta_offset (self , offset ):
381+ """
382+ Set the offset for the location of 0 in radians.
383+ """
384+ self ._theta_offset = offset
385+
386+ def get_theta_offset (self ):
387+ """
388+ Get the offset for the location of 0 in radians.
389+ """
390+ return self ._theta_offset
391+
392+ def set_theta_zero_location (self , loc ):
393+ """
394+ Sets the location of theta's zero. (Calls set_theta_offset
395+ with the correct value in radians under the hood.)
396+
397+ May be one of "N", "NW", "W", "SW", "S", "SE", "E", or "NE".
398+ """
399+ mapping = {
400+ 'N' : np .pi * 0.5 ,
401+ 'NW' : np .pi * 0.75 ,
402+ 'W' : np .pi ,
403+ 'SW' : np .pi * 1.25 ,
404+ 'S' : np .pi * 1.5 ,
405+ 'SE' : np .pi * 1.75 ,
406+ 'E' : 0 ,
407+ 'NE' : np .pi * 0.25 }
408+ return self .set_theta_offset (mapping [loc ])
409+
410+ def set_theta_direction (self , direction ):
411+ """
412+ Set the direction in which theta increases.
413+
414+ clockwise, -1:
415+ Theta increases in the clockwise direction
416+
417+ counterclockwise, anticlockwise, 1:
418+ Theta increases in the counterclockwise direction
419+ """
420+ if direction in ('clockwise' ,):
421+ self ._direction = - 1
422+ elif direction in ('counterclockwise' , 'anticlockwise' ):
423+ self ._direction = 1
424+ elif direction in (1 , - 1 ):
425+ self ._direction = direction
426+ else :
427+ raise ValueError ("direction must be 1, -1, clockwise or counterclockwise" )
428+
429+ def get_theta_direction (self ):
430+ """
431+ Get the direction in which theta increases.
432+
433+ -1:
434+ Theta increases in the clockwise direction
435+
436+ 1:
437+ Theta increases in the counterclockwise direction
438+ """
439+ return self ._direction
440+
349441 def set_rlim (self , * args , ** kwargs ):
350442 if 'rmin' in kwargs :
351443 kwargs ['ymin' ] = kwargs .pop ('rmin' )
0 commit comments