14
14
import matplotlib .axis as maxis
15
15
from matplotlib import cbook
16
16
from matplotlib import docstring
17
- from matplotlib .patches import Circle
17
+ from matplotlib .patches import Wedge
18
18
from matplotlib .path import Path
19
19
from matplotlib .ticker import Formatter , Locator , FormatStrFormatter
20
20
from matplotlib .transforms import Affine2D , Affine2DBase , Bbox , \
21
21
BboxTransformTo , IdentityTransform , Transform , TransformWrapper , \
22
- ScaledTranslation , blended_transform_factory , BboxTransformToMaxOnly
22
+ ScaledTranslation , blended_transform_factory , BboxTransformToMaxOnly , \
23
+ TransformedBbox
23
24
import matplotlib .spines as mspines
24
25
25
26
@@ -43,7 +44,7 @@ def transform_non_affine(self, tr):
43
44
xy = np .empty (tr .shape , np .float_ )
44
45
if self ._axis is not None :
45
46
if self ._use_rmin :
46
- rmin = self ._axis .viewLim . ymin
47
+ rmin = self ._axis .get_rorigin ()
47
48
else :
48
49
rmin = 0
49
50
theta_offset = self ._axis .get_theta_offset ()
@@ -130,7 +131,7 @@ def __init__(self, axis=None, use_rmin=True):
130
131
def transform_non_affine (self , xy ):
131
132
if self ._axis is not None :
132
133
if self ._use_rmin :
133
- rmin = self ._axis .viewLim . ymin
134
+ rmin = self ._axis .get_rorigin ()
134
135
else :
135
136
rmin = 0
136
137
theta_offset = self ._axis .get_theta_offset ()
@@ -216,6 +217,29 @@ def view_limits(self, vmin, vmax):
216
217
return min (0 , vmin ), vmax
217
218
218
219
220
+ class _RadialOffsetTransform (Transform ):
221
+ """
222
+ Adjust Bbox so that is spans from the Origin to the Maximum in data space
223
+ coordinates, instead of Minimum to Maximum.
224
+ """
225
+ input_dims = 2
226
+ output_dims = 2
227
+ is_separable = False
228
+
229
+ def __init__ (self , axis ):
230
+ Transform .__init__ (self )
231
+ self ._axis = axis
232
+
233
+ def transform_non_affine (self , tr ):
234
+ xy = tr .copy ()
235
+ rmin = self ._axis ._rorigin
236
+ if rmin is not None :
237
+ # This is expected to be applied to only the viewLim Bbox.
238
+ xy [0 , 1 ] = rmin
239
+ return xy
240
+ transform_non_affine .__doc__ = Transform .transform_non_affine .__doc__
241
+
242
+
219
243
class PolarAxes (Axes ):
220
244
"""
221
245
A polar graph projection, where the input dimensions are *theta*, *r*.
@@ -238,6 +262,9 @@ def __init__(self, *args, **kwargs):
238
262
self ._default_theta_offset = kwargs .pop ('theta_offset' , 0 )
239
263
self ._default_theta_direction = kwargs .pop ('theta_direction' , 1 )
240
264
self ._default_rlabel_position = kwargs .pop ('rlabel_position' , 22.5 )
265
+ self ._rorigin = None
266
+ self ._rotransform = _RadialOffsetTransform (self )
267
+ self ._rotransform ._parents [id (self )] = self # Hack?
241
268
242
269
if self .resolution not in (None , 1 ):
243
270
warnings .warn (
@@ -262,11 +289,13 @@ def cla(self):
262
289
263
290
self .grid (rcParams ['polaraxes.grid' ])
264
291
self .xaxis .set_ticks_position ('none' )
292
+ self .spines ['inner' ].set_visible (False )
265
293
self .yaxis .set_ticks_position ('none' )
266
294
self .yaxis .set_tick_params (label1On = True )
267
295
# Why do we need to turn on yaxis tick labels, but
268
296
# xaxis tick labels are already on?
269
297
298
+ self ._rorigin = None
270
299
self .set_theta_offset (self ._default_theta_offset )
271
300
self .set_theta_direction (self ._default_theta_direction )
272
301
@@ -296,7 +325,9 @@ def _set_lim_and_transforms(self):
296
325
297
326
# An affine transformation on the data, generally to limit the
298
327
# range of the axes
299
- self .transProjectionAffine = self .PolarAffine (self .transScale , self .viewLim )
328
+ self ._offsetViewLim = TransformedBbox (self .viewLim , self ._rotransform )
329
+ self .transProjectionAffine = self .PolarAffine (self .transScale ,
330
+ self ._offsetViewLim )
300
331
301
332
# The complete data transformation stack -- from data all the
302
333
# way to display coordinates
@@ -375,24 +406,36 @@ def get_yaxis_text2_transform(self, pad):
375
406
else :
376
407
return self ._yaxis_text_transform , 'bottom' , 'right'
377
408
378
- def _gen_axes_patch (self ):
379
- return Circle ((0.5 , 0.5 ), 0.5 )
380
-
381
- def _gen_axes_spines (self ):
382
- return {'polar' :mspines .Spine .circular_spine (self ,
383
- (0.5 , 0.5 ), 0.5 )}
409
+ def _invalidate_internal (self , * args , ** kwargs ):
410
+ rmin , rmax = self .viewLim .intervaly
411
+ rorigin = self .get_rorigin ()
412
+ inner = self .spines .get ('inner' , None )
413
+
414
+ if rorigin < rmin :
415
+ width = (rmax - rmin ) / (rmax - rorigin ) * 0.5
416
+ self .patch .set_width (width )
417
+ if inner :
418
+ inner .set_visible (True )
419
+ inner .set_patch_circle ((0.5 , 0.5 ), 0.5 - width )
420
+ else :
421
+ self .patch .set_width (0.5 )
422
+ if inner :
423
+ inner .set_patch_circle ((0.5 , 0.5 ), 0.0 )
424
+ inner .set_visible (False )
384
425
385
- def set_rmax (self , rmax ):
386
- self .viewLim .y1 = rmax
426
+ for line in self .lines :
427
+ self ._update_line_limits (line )
428
+ for p in self .patches :
429
+ self ._update_patch_limits (p )
387
430
388
- def get_rmax (self ):
389
- return self .viewLim .ymax
390
-
391
- def set_rmin (self , rmin ):
392
- self .viewLim .y0 = rmin
431
+ def _gen_axes_patch (self ):
432
+ return Wedge ((0.5 , 0.5 ), 0.5 , 0.0 , 360.0 )
393
433
394
- def get_rmin (self ):
395
- return self .viewLim .ymin
434
+ def _gen_axes_spines (self ):
435
+ return {'outer' : mspines .Spine .circular_spine (self ,
436
+ (0.5 , 0.5 ), 0.5 ),
437
+ 'inner' : mspines .Spine .circular_spine (self ,
438
+ (0.5 , 0.5 ), 0.0 )}
396
439
397
440
def set_theta_offset (self , offset ):
398
441
"""
@@ -455,6 +498,25 @@ def get_theta_direction(self):
455
498
"""
456
499
return self ._direction
457
500
501
+ def set_rmax (self , rmax ):
502
+ self .viewLim .y1 = rmax
503
+
504
+ def get_rmax (self ):
505
+ return self .viewLim .ymax
506
+
507
+ def set_rmin (self , rmin ):
508
+ self .viewLim .y0 = rmin
509
+
510
+ def get_rmin (self ):
511
+ return self .viewLim .ymin
512
+
513
+ def set_rorigin (self , rorigin ):
514
+ self ._rorigin = rorigin
515
+ self ._rotransform .invalidate ()
516
+
517
+ def get_rorigin (self ):
518
+ return self ._rorigin or self .get_rmin ()
519
+
458
520
def set_rlim (self , * args , ** kwargs ):
459
521
if 'rmin' in kwargs :
460
522
kwargs ['ymin' ] = kwargs .pop ('rmin' )
0 commit comments