我想创建一个类似matplotlib.patches.PathPatch
的面片,线宽以数据单位(而不是点)表示。
我知道,对于具有规则形状的面片,可以通过在彼此顶部绘制两个不同大小的面片来模拟这样的对象。
然而,对于任意形状,这种方法在计算上要复杂得多(需要计算任意形状的平行线)。
此外,我希望保留PathPath
对象方法,因此最终我要寻找从PathPatch
或Patch
派生的类
import matplotlib.pyplot as plt
from matplotlib.patches import PathPatch, Rectangle
fig, (ax1, ax2) = plt.subplots(1, 2, sharex=True, sharey=True)
origin = (0, 0)
width = 1
height = 2
lw = 0.25
outer = Rectangle((origin[0], origin[1]), width, height, facecolor='darkblue', zorder=1)
inner = Rectangle((origin[0]+lw, origin[1]+lw), width-2*lw, height-2*lw, facecolor='lightblue', zorder=2)
ax1.add_patch(outer)
ax1.add_patch(inner)
ax1.axis([-0.5, 1.5, -0.5, 2.5])
ax1.set_title('Desired')
path = outer.get_path().transformed(outer.get_patch_transform())
pathpatch = PathPatch(path, facecolor='lightblue', edgecolor='darkblue', linewidth=lw)
ax2.add_patch(pathpatch)
ax2.set_title('Actual')
plt.show()
有一个从Line2D
派生的different SO post来创建以数据单位为宽度的行
class LineDataUnits(Line2D):
# https://stackoverflow.com/a/42972469/2912349
def __init__(self, *args, **kwargs):
_lw_data = kwargs.pop("linewidth", 1)
super().__init__(*args, **kwargs)
self._lw_data = _lw_data
def _get_lw(self):
if self.axes is not None:
ppd = 72./self.axes.figure.dpi
trans = self.axes.transData.transform
return ((trans((1, self._lw_data))-trans((0, 0)))*ppd)[1]
else:
return 1
def _set_lw(self, lw):
self._lw_data = lw
_linewidth = property(_get_lw, _set_lw)
我不太明白这个实现是如何工作的(例如,Line2D
的任何方法似乎都不会被覆盖)
尽管如此,我还是尝试了复制这种方法(class LineDataUnits(Line2D)
->;class PathPatchDataUnits(PathPatch)
),但是——毫不奇怪——无法让它工作(AttributeError: 'NoneType' object has no attribute 'set_figure'
)
编辑:
我成功了,并在途中获得了一些见解。 首先,代码如下:
结果是:
现在让我想想:
axes.transData
变换实际上是由两个变换组成的,一个用于x坐标,另一个用于y坐标。 但是,线宽是单个标量,因此我们需要选择将使用的两种变换中的哪一种。 在我的解决方案中,我使用y变换;但是,通过将注释下方的行更改为索引[0],可以选择使用x变换。 如果轴的纵横比相等(即,数据单位中长度为1的水平线在显示单位中的长度与数据单位中长度为1的垂直线的长度相同), 这不是问题。但是,如果纵横比不相等,则可能会产生下图所示的意外行为可以使用命令
ax.set_aspect('equal')
强制轴的纵横比相等相关问题 更多 >
编程相关推荐