@@ -370,6 +370,96 @@ def view_limits(self, vmin, vmax):
370
370
return mtransforms .nonsingular (min (0 , vmin ), vmax )
371
371
372
372
373
+ class RadialTick (maxis .YTick ):
374
+ """
375
+ A radial-axis tick.
376
+
377
+ This subclass of `YTick` provides radial ticks with some small modification
378
+ to their re-positioning such that ticks are rotated based on axes limits.
379
+ This results in ticks that are correctly perpendicular to the spine. Labels
380
+ are also rotated to be perpendicular to the spine.
381
+ """
382
+ def _get_text1 (self ):
383
+ t = maxis .YTick ._get_text1 (self )
384
+ t .set_rotation_mode ('anchor' )
385
+ return t
386
+
387
+ def _get_text2 (self ):
388
+ t = maxis .YTick ._get_text2 (self )
389
+ t .set_rotation_mode ('anchor' )
390
+ return t
391
+
392
+ def update_position (self , loc ):
393
+ maxis .YTick .update_position (self , loc )
394
+ axes = self .axes
395
+ thetamin = axes .get_thetamin ()
396
+ thetamax = axes .get_thetamax ()
397
+ direction = axes .get_theta_direction ()
398
+ offset_rad = axes .get_theta_offset ()
399
+ offset = np .rad2deg (offset_rad )
400
+ full = _is_full_circle_deg (thetamin , thetamax )
401
+
402
+ if full :
403
+ angle = axes .get_rlabel_position () * direction + offset - 90
404
+ tick_angle = np .deg2rad (angle )
405
+ else :
406
+ angle = thetamin * direction + offset - 90
407
+ if direction > 0 :
408
+ tick_angle = np .deg2rad (angle )
409
+ else :
410
+ tick_angle = np .deg2rad (angle + 180 )
411
+ if self .label1On :
412
+ self .label1 .set_rotation (angle + self ._labelrotation )
413
+ if self .tick1On :
414
+ marker = self .tick1line .get_marker ()
415
+ if marker == mmarkers .TICKLEFT :
416
+ trans = (mtransforms .Affine2D ()
417
+ .scale (1.0 , 1.0 )
418
+ .rotate (tick_angle ))
419
+ elif marker == '_' :
420
+ trans = (mtransforms .Affine2D ()
421
+ .scale (1.0 , 1.0 )
422
+ .rotate (tick_angle + np .pi / 2 ))
423
+ elif marker == mmarkers .TICKRIGHT :
424
+ trans = (mtransforms .Affine2D ()
425
+ .scale (- 1.0 , 1.0 )
426
+ .rotate (tick_angle ))
427
+ else :
428
+ # Don't modify custom tick line markers.
429
+ trans = self .tick1line ._marker ._transform
430
+ self .tick1line ._marker ._transform = trans
431
+
432
+ if full :
433
+ self .label2On = False
434
+ self .tick2On = False
435
+ else :
436
+ angle = thetamax * direction + offset - 90
437
+ if direction > 0 :
438
+ tick_angle = np .deg2rad (angle )
439
+ else :
440
+ tick_angle = np .deg2rad (angle + 180 )
441
+ if self .label2On :
442
+ self .label2 .set_rotation (angle + self ._labelrotation )
443
+ if self .tick2On :
444
+ marker = self .tick2line .get_marker ()
445
+ if marker == mmarkers .TICKLEFT :
446
+ trans = (mtransforms .Affine2D ()
447
+ .scale (1.0 , 1.0 )
448
+ .rotate (tick_angle ))
449
+ elif marker == '_' :
450
+ trans = (mtransforms .Affine2D ()
451
+ .scale (1.0 , 1.0 )
452
+ .rotate (tick_angle + np .pi / 2 ))
453
+ elif marker == mmarkers .TICKRIGHT :
454
+ trans = (mtransforms .Affine2D ()
455
+ .scale (- 1.0 , 1.0 )
456
+ .rotate (tick_angle ))
457
+ else :
458
+ # Don't modify custom tick line markers.
459
+ trans = self .tick2line ._marker ._transform
460
+ self .tick2line ._marker ._transform = trans
461
+
462
+
373
463
class RadialAxis (maxis .YAxis ):
374
464
"""
375
465
A radial Axis.
@@ -385,7 +475,7 @@ def _get_tick(self, major):
385
475
tick_kw = self ._major_tick_kw
386
476
else :
387
477
tick_kw = self ._minor_tick_kw
388
- return maxis . YTick (self .axes , 0 , '' , major = major , ** tick_kw )
478
+ return RadialTick (self .axes , 0 , '' , major = major , ** tick_kw )
389
479
390
480
def _wrap_locator_formatter (self ):
391
481
self .set_major_locator (RadialLocator (self .get_major_locator (),
@@ -657,50 +747,16 @@ def get_yaxis_transform(self, which='grid'):
657
747
def get_yaxis_text1_transform (self , pad ):
658
748
thetamin , thetamax = self ._realViewLim .intervalx
659
749
full = _is_full_circle_rad (thetamin , thetamax )
660
- if full :
661
- angle = self .get_rlabel_position ()
750
+ if self . get_theta_direction () > 0 or full :
751
+ return self ._yaxis_text_transform , 'center' , 'left'
662
752
else :
663
- angle = np .rad2deg (thetamin )
664
- if angle < 0 :
665
- angle += 360
666
- angle %= 360
667
-
668
- # NOTE: Due to a bug, previous code always used bottom left, contrary
669
- # to its original intentions here.
670
- valign = [['top' , 'bottom' , 'bottom' , 'top' ],
671
- # ['bottom', 'bottom', 'top', 'top']]
672
- ['bottom' , 'bottom' , 'bottom' , 'bottom' ]]
673
- halign = [['left' , 'left' , 'right' , 'right' ],
674
- # ['left', 'right', 'right', 'left']]
675
- ['left' , 'left' , 'left' , 'left' ]]
676
-
677
- ind = np .digitize ([angle ], np .arange (0 , 361 , 90 ))[0 ] - 1
678
-
679
- return self ._yaxis_text_transform , valign [full ][ind ], halign [full ][ind ]
753
+ return self ._yaxis_text_transform , 'center' , 'right'
680
754
681
755
def get_yaxis_text2_transform (self , pad ):
682
- thetamin , thetamax = self ._realViewLim .intervalx
683
- full = _is_full_circle_rad (thetamin , thetamax )
684
- if full :
685
- angle = self .get_rlabel_position ()
756
+ if self .get_theta_direction () > 0 :
757
+ return self ._yaxis_text_transform , 'center' , 'right'
686
758
else :
687
- angle = np .rad2deg (thetamax )
688
- if angle < 0 :
689
- angle += 360
690
- angle %= 360
691
-
692
- # NOTE: Due to a bug, previous code always used top right, contrary to
693
- # its original intentions here.
694
- valign = [['bottom' , 'top' , 'top' , 'bottom' ],
695
- # ['top', 'top', 'bottom', 'bottom']]
696
- ['top' , 'top' , 'top' , 'top' ]]
697
- halign = [['right' , 'right' , 'left' , 'left' ],
698
- # ['right', 'left', 'left', 'right']]
699
- ['right' , 'right' , 'right' , 'right' ]]
700
-
701
- ind = np .digitize ([angle ], np .arange (0 , 361 , 90 ))[0 ] - 1
702
-
703
- return self ._yaxis_text_transform , valign [full ][ind ], halign [full ][ind ]
759
+ return self ._yaxis_text_transform , 'center' , 'left'
704
760
705
761
def draw (self , * args , ** kwargs ):
706
762
thetamin , thetamax = self ._realViewLim .intervalx
@@ -845,6 +901,9 @@ def set_theta_direction(self, direction):
845
901
raise ValueError (
846
902
"direction must be 1, -1, clockwise or counterclockwise" )
847
903
self ._direction .invalidate ()
904
+ # FIXME: Why is this needed? Even though the tick label gets
905
+ # re-created, the alignment is not correctly updated without a reset.
906
+ self .yaxis .reset_ticks ()
848
907
849
908
def get_theta_direction (self ):
850
909
"""
@@ -901,6 +960,7 @@ def set_rlabel_position(self, value):
901
960
The angular position of the radius labels in degrees.
902
961
"""
903
962
self ._r_label_position .clear ().translate (np .deg2rad (value ), 0.0 )
963
+ self .yaxis .reset_ticks ()
904
964
905
965
def set_yscale (self , * args , ** kwargs ):
906
966
Axes .set_yscale (self , * args , ** kwargs )
0 commit comments