路径教程
在可视化的Matplotlib中定义路径。
所有matplotlib.patches对象的基础对象是Path,它支持一组标准的moveto、lineto和curveto命令,以绘制由直线段和曲线组成的简单和复合轮廓。Path用N个顶点和N条Path命令实例化的,这N个顶点用一个二维数组表示,每一行表示一个顶点的坐标(x, y);N条命令也用一个一维数组表示。例如,要绘制从(0, 0)到(1, 1)的单位矩形,我们可以使用以下代码:
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
import matplotlib.pyplot as pltfrom matplotlib.path import Pathimport matplotlib.patches as patches
verts = [ (0., 0.), # left, bottom (0., 1.), # left, top (1., 1.), # right, top (1., 0.), # right, bottom (0., 0.), # ignored]
codes = [ Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY,]
path = Path(verts, codes)
fig, ax = plt.subplots()patch = patches.PathPatch(path, facecolor='orange', lw=2)ax.add_patch(patch)ax.set_xlim(-2, 2)ax.set_ylim(-2, 2)plt.show()
下面的Path命令需要记住:
命令 | 顶点数 | 描述 |
STOP | 1(忽略参数) | 整个Path的结束标记(目前不需要这个命令,且已被matplotlib忽略)。 |
MOVETO | 1 | 抬笔并移动到给定的顶点。 |
LINETO | 1 | 从当前位置绘制直线段到给定的顶点。 |
CURVE3 | 2:一个控制点,一个终点。 | 从当前位置,过一个控制点,绘制一条二次贝塞尔曲线(quadratic Bézier curve)到给定的顶点。 |
CURVE4 | 3:二个控制点,一个终点。 | 从当前位置,过二个控制点,绘制一条三次贝塞尔曲线(cubic Bézier curve)到给定的顶点。 |
CLOSEPOLY | 1(该顶点被省略) | 绘制到当前多线段图形的起顶点(简单说,就是闭环。) |
Bézier(贝塞尔)示例
一些Path组件需要多个顶点来指定它们:例如,CURVE 3是一条具有一个控制点和一个终点的Bézier曲线,而CURVE 4具有两个控制点和一个终点共三个顶点。下面的示例显示了一个CURVE4 Bézier曲线——Bézier曲线将包含在起点、两个控制点和终点的凸包中。
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
import matplotlib.pyplot as pltfrom matplotlib.path import Pathimport matplotlib.patches as patches
verts = [ (0., 0.), # P0 (0.2, 1.), # P1 (1., 0.8), # P2 (0.8, 0.), # P3]
codes = [ Path.MOVETO, Path.CURVE4, Path.CURVE4, Path.CURVE4,]
path = Path(verts, codes)
fig, ax = plt.subplots()patch = patches.PathPatch(path, facecolor='none', lw=2)ax.add_patch(patch)
xs, ys = zip(*verts)ax.plot(xs, ys, 'x--', lw=2, color='black', ms=10)
ax.text(-0.05, -0.05, 'P0')ax.text(0.15, 1.05, 'P1')ax.text(1.05, 0.85, 'P2')ax.text(0.85, -0.05, 'P3')
ax.set_xlim(-0.1, 1.1)ax.set_ylim(-0.1, 1.1)plt.show()
复合Paths
matplotlib、Rectangle、Circle、Polygon等中的所有简单面片基元都是由简单Path命令实现的。像hist()和bar()这样的绘图函数可以创建许多基元,例如一组矩形,通常可以使用复合Path更有效地实现。bar(条形图)创建矩形列表而不是复合Path的原因在很大程度上是历史遗留问题——Path代码相对较新,条形图早于它。虽然我们现在可以更改它,但它会破坏旧代码,所以在这里我们将介绍如何创建复合Paths,替换条形图功能,以备出于效率原因需要在自己的代码中这样做,例如,要创建一个动画条形图。
我们将通过为每个直方图条(histogram bar)创建一系列矩形来制作直方图(histogram chart):矩形宽度是区间(bin)的宽度,矩形高度是区间中数据点的数量。首先,我们将创建一些随机正态分布的数据,并计算直方图。因为NumPy返回的是区间边缘而不是中心,所以在下面的示例中,区间的长度比n的长度大1:
·
·
·
# histogram our data with numpydata = np.random.randn(1000)n, bins = np.histogram(data, 100)
现在我们将提取矩形的顶角。下面的左侧、底部等数组中的每一个的长度都是len(n),其中n是每个直方图条的计数数组:
·
·
·
·
·
# get the corners of the rectangles for the histogramleft = np.array(bins[:-1])right = np.array(bins[1:])bottom = np.zeros(len(left))top = bottom + n
现在我们必须构建我们的复合Path,它将由每个矩形的一系列MOVETO、LINETO和CLOSEPOLY组成。对于每个矩形,我们需要五个顶点:一个用于MOVETO,三个用于LINETO,一个用于CLOSEPOLY。如上表所示,闭合多边形的顶点被忽略,但我们仍然需要它来保持代码与顶点对齐:
·
·
·
·
·
·
·
·
·
·
·
·
·
nverts = nrects*(1+3+1)verts = np.zeros((nverts, 2))codes = np.ones(nverts, int) * path.Path.LINETOcodes[0::5] = path.Path.MOVETOcodes[4::5] = path.Path.CLOSEPOLYverts[0::5, 0] = leftverts[0::5, 1] = bottomverts[1::5, 0] = leftverts[1::5, 1] = topverts[2::5, 0] = rightverts[2::5, 1] = topverts[3::5, 0] = rightverts[3::5, 1] = bottom
剩下的就是创建Path,将其附加到PathPatch,然后将其添加到我们的图表域(axes):
·
·
·
·
barpath = path.Path(verts, codes)patch = patches.PathPatch(barpath, facecolor='green', edgecolor='yellow', alpha=0.5)ax.add_patch(patch)
完整代码:
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
import numpy as npimport matplotlib.patches as patchesimport matplotlib.path as path
fig, ax = plt.subplots()# Fixing random state for reproducibilitynp.random.seed(19680801)
# histogram our data with numpydata = np.random.randn(1000)n, bins = np.histogram(data, 100)
# get the corners of the rectangles for the histogramleft = np.array(bins[:-1])right = np.array(bins[1:])bottom = np.zeros(len(left))top = bottom + nnrects = len(left)
nverts = nrects*(1+3+1)verts = np.zeros((nverts, 2))codes = np.ones(nverts, int) * path.Path.LINETOcodes[0::5] = path.Path.MOVETOcodes[4::5] = path.Path.CLOSEPOLYverts[0::5, 0] = leftverts[0::5, 1] = bottomverts[1::5, 0] = leftverts[1::5, 1] = topverts[2::5, 0] = rightverts[2::5, 1] = topverts[3::5, 0] = rightverts[3::5, 1] = bottom
barpath = path.Path(verts, codes)patch = patches.PathPatch(barpath, facecolor='green', edgecolor='yellow', alpha=0.5)ax.add_patch(patch)
ax.set_xlim(left[0], right[-1])ax.set_ylim(bottom.min(), top.max())
plt.show()
matplotlib.patches:https://matplotlib.org/stable/api/patches_api.html#module-matplotlib.patchesPath:https://matplotlib.org/stable/api/path_api.html#matplotlib.path.Pathhist():https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.hist.html#matplotlib.axes.Axes.histbar():https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.bar.html#matplotlib.axes.Axes.barPathPatch:https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.PathPatch.html#matplotlib.patches.PathPatch