您可以使用以下类来创建沿线的文本。而不是一个角度,它需要两个点 (p
和pa
) 作为输入。这两个点之间的连接定义了数据坐标中的角度。如果pa
未给出,则使用p
和xy
(文本坐标)之间的连接线。
然后角度会自动更新,以使文本始终沿线定向。这甚至适用于对数刻度。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.text as mtext
import matplotlib.transforms as mtransforms
class RotationAwareAnnotation(mtext.Annotation):
def __init__(self, s, xy, p, pa=None, ax=None, **kwargs):
self.ax = ax or plt.gca()
self.p = p
if not pa:
self.pa = xy
self.calc_angle_data()
kwargs.update(rotation_mode=kwargs.get("rotation_mode", "anchor"))
mtext.Annotation.__init__(self, s, xy, **kwargs)
self.set_transform(mtransforms.IdentityTransform())
if 'clip_on' in kwargs:
self.set_clip_path(self.ax.patch)
self.ax._add_text(self)
def calc_angle_data(self):
ang = np.arctan2(self.p[1]-self.pa[1], self.p[0]-self.pa[0])
self.angle_data = np.rad2deg(ang)
def _get_rotation(self):
return self.ax.transData.transform_angles(np.array((self.angle_data,)),
np.array([self.pa[0], self.pa[1]]).reshape((1, 2)))[0]
def _set_rotation(self, rotation):
pass
_rotation = property(_get_rotation, _set_rotation)
示例用法:
fig, ax = plt.subplots()
t = np.arange(0.0, 1.0, 0.01)
line, = ax.plot(t, t, color='blue', lw=2)
ra = RotationAwareAnnotation("test label", xy=(.5,.5), p=(.6,.6), ax=ax,
xytext=(2,-1), textcoords="offset points", va="top")
plt.show()
![在此处输入图像描述](https://i.stack.imgur.com/nijit.gif)
边缘情况的替代方案
在某些情况下,沿垂直线的文本或具有高度不同的 x 和 y 单位的比例尺(例如此处),上述方法可能会失败。在这种情况下,以下将更适合。它计算屏幕坐标中的角度,而不是依赖于角度变换。
class RotationAwareAnnotation2(mtext.Annotation):
def __init__(self, s, xy, p, pa=None, ax=None, **kwargs):
self.ax = ax or plt.gca()
self.p = p
if not pa:
self.pa = xy
kwargs.update(rotation_mode=kwargs.get("rotation_mode", "anchor"))
mtext.Annotation.__init__(self, s, xy, **kwargs)
self.set_transform(mtransforms.IdentityTransform())
if 'clip_on' in kwargs:
self.set_clip_path(self.ax.patch)
self.ax._add_text(self)
def calc_angle(self):
p = self.ax.transData.transform_point(self.p)
pa = self.ax.transData.transform_point(self.pa)
ang = np.arctan2(p[1]-pa[1], p[0]-pa[0])
return np.rad2deg(ang)
def _get_rotation(self):
return self.calc_angle()
def _set_rotation(self, rotation):
pass
_rotation = property(_get_rotation, _set_rotation)
在通常情况下,两者都会产生相同的输出。我不确定二等舱是否有任何缺点,所以我将两者都留在这里,选择你认为更合适的那个。