Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 06e887d

Browse filesBrowse files
committed
Add a custom RadialTick for polar plots.
Just like ThetaTick, these allow for text and tick to be correctly perpendicular to the spine.
1 parent bef46ab commit 06e887d
Copy full SHA for 06e887d

File tree

Expand file treeCollapse file tree

1 file changed

+85
-41
lines changed
Filter options
Expand file treeCollapse file tree

1 file changed

+85
-41
lines changed

‎lib/matplotlib/projections/polar.py

Copy file name to clipboardExpand all lines: lib/matplotlib/projections/polar.py
+85-41Lines changed: 85 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,80 @@ def view_limits(self, vmin, vmax):
370370
return mtransforms.nonsingular(min(0, vmin), vmax)
371371

372372

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 in (mmarkers.TICKLEFT, mmarkers.TICKRIGHT, '_'):
416+
trans = (mtransforms.Affine2D()
417+
.scale(1.0, 1.0)
418+
.rotate(tick_angle))
419+
else:
420+
# Don't modify custom tick line markers.
421+
trans = self.tick1line._marker._transform
422+
self.tick1line._marker._transform = trans
423+
424+
if full:
425+
self.label2On = False
426+
self.tick2On = False
427+
else:
428+
angle = thetamax * direction + offset - 90
429+
if direction > 0:
430+
tick_angle = np.deg2rad(angle + 180)
431+
else:
432+
tick_angle = np.deg2rad(angle)
433+
if self.label2On:
434+
self.label2.set_rotation(angle + self._labelrotation)
435+
if self.tick2On:
436+
marker = self.tick2line.get_marker()
437+
if marker in (mmarkers.TICKLEFT, mmarkers.TICKRIGHT, '_'):
438+
trans = (mtransforms.Affine2D()
439+
.scale(1.0, 1.0)
440+
.rotate(tick_angle))
441+
else:
442+
# Don't modify custom tick line markers.
443+
trans = self.tick2line._marker._transform
444+
self.tick2line._marker._transform = trans
445+
446+
373447
class RadialAxis(maxis.YAxis):
374448
"""
375449
A radial Axis.
@@ -385,7 +459,7 @@ def _get_tick(self, major):
385459
tick_kw = self._major_tick_kw
386460
else:
387461
tick_kw = self._minor_tick_kw
388-
return maxis.YTick(self.axes, 0, '', major=major, **tick_kw)
462+
return RadialTick(self.axes, 0, '', major=major, **tick_kw)
389463

390464
def _wrap_locator_formatter(self):
391465
self.set_major_locator(RadialLocator(self.get_major_locator(),
@@ -657,50 +731,16 @@ def get_yaxis_transform(self, which='grid'):
657731
def get_yaxis_text1_transform(self, pad):
658732
thetamin, thetamax = self._realViewLim.intervalx
659733
full = _is_full_circle_rad(thetamin, thetamax)
660-
if full:
661-
angle = self.get_rlabel_position()
734+
if self.get_theta_direction() > 0 or full:
735+
return self._yaxis_text_transform, 'center', 'left'
662736
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]
737+
return self._yaxis_text_transform, 'center', 'right'
680738

681739
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()
740+
if self.get_theta_direction() > 0:
741+
return self._yaxis_text_transform, 'center', 'right'
686742
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]
743+
return self._yaxis_text_transform, 'center', 'left'
704744

705745
def draw(self, *args, **kwargs):
706746
thetamin, thetamax = self._realViewLim.intervalx
@@ -845,6 +885,9 @@ def set_theta_direction(self, direction):
845885
raise ValueError(
846886
"direction must be 1, -1, clockwise or counterclockwise")
847887
self._direction.invalidate()
888+
# FIXME: Why is this needed? Even though the tick label gets
889+
# re-created, the alignment is not correctly updated without a reset.
890+
self.yaxis.reset_ticks()
848891

849892
def get_theta_direction(self):
850893
"""
@@ -901,6 +944,7 @@ def set_rlabel_position(self, value):
901944
The angular position of the radius labels in degrees.
902945
"""
903946
self._r_label_position.clear().translate(np.deg2rad(value), 0.0)
947+
self.yaxis.reset_ticks()
904948

905949
def set_yscale(self, *args, **kwargs):
906950
Axes.set_yscale(self, *args, **kwargs)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.