`matplotlib.patches.PathPatch`的线宽以数据单位(非点)表示

2024-04-27 04:54:57 发布

您现在位置:Python中文网/ 问答频道 /正文

我想创建一个类似matplotlib.patches.PathPatch的面片,线宽以数据单位(而不是点)表示。

我知道,对于具有规则形状的面片,可以通过在彼此顶部绘制两个不同大小的面片来模拟这样的对象。 然而,对于任意形状,这种方法在计算上要复杂得多(需要计算任意形状的平行线)。 此外,我希望保留PathPath对象方法,因此最终我要寻找从PathPatchPatch派生的类

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()

enter image description here

有一个从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'


Tags: 方法selfdatagetmatplotliboriginpatch形状
1条回答
网友
1楼 · 发布于 2024-04-27 04:54:57

编辑:

我成功了,并在途中获得了一些见解。 首先,代码如下:

import matplotlib.pyplot as plt
from matplotlib.patches import PathPatch, Rectangle

# The class from the solution you found, I just changed inheritance 
class PatchDataUnits(PathPatch):
    # 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
            # the line mentioned below
            return ((trans((self._lw_data, 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)

# Your code example
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')

# change lw because it overlaps some of the fig
mlw = lw /2 
# create new patch with the adusted size
mid = Rectangle((origin[0]+mlw, origin[1]+mlw), width-2*mlw, height-2*mlw, facecolor='lightblue',  zorder=1)
path = mid.get_path().transformed(mid.get_patch_transform())
# Use the data unit class to create the path patch with the original lw
pathpatch = PatchDataUnits(path, facecolor='lightblue', edgecolor='darkblue', linewidth=lw)
ax2.add_patch(pathpatch)
ax2.set_title('Actual')

plt.show()

结果是: The results I got on my machine

现在让我想想:

axes.transData变换实际上是由两个变换组成的,一个用于x坐标,另一个用于y坐标。 但是,线宽是单个标量,因此我们需要选择将使用的两种变换中的哪一种。 在我的解决方案中,我使用y变换;但是,通过将注释下方的行更改为索引[0],可以选择使用x变换。 如果轴的纵横比相等(即,数据单位中长度为1的水平线在显示单位中的长度与数据单位中长度为1的垂直线的长度相同), 这不是问题。但是,如果纵横比不相等,则可能会产生下图所示的意外行为

enter image description here

可以使用命令ax.set_aspect('equal')强制轴的纵横比相等

相关问题 更多 >