加入QQ交流群

喜欢本站?打赏作者


日期:2024年10月2日

Python bezier库超详细教程

bezier是Python的一个第三方库,可以让我们方便地处理、绘制贝塞尔曲线。使用前需要用pip安装。

pip install bezier(使用国外的默认源,在国内使用下载速度慢)
pip install bezier -i https://pypi.tuna.tsinghua.edu.cn/simple(使用国内的清华镜像源,速度更快)

官方文档链接

官方文档中给出的贝塞尔曲线公式中参数叫s,但大多数说明中都用的是t,本文中我选择直接称其“参数”。

节点格式

bezier库中节点的排列格式都是[[x1, x2, ..., xn], [y1, y2, ..., yn], [z1, z2, ..., zn]],这么做是为了贴合matplotlib库画线时用的格式,而很多地方都是用[[x1, y1, z1], [x2, y2, z2], ..., [xn, yn, zn]]这种格式,我们可以通过解压序列轻松完成转换。

>>> # [[x, y]]转[[x], [y]]
>>> original = [[0.0, 0.0], [0.625, 0.5], [1.0, 0.5]]
>>> new = list(zip(*original))
>>> new
[(0.0, 0.625, 1.0), (0.0, 0.5, 0.5)]

>>> # [[x], [y]]转[[x, y]]
>>> original = [[0.0, 0.625, 1.0], [0.0, 0.5, 0.5]]
>>> new = list(zip(*original))
>>> new
[(0.0, 0.0), (0.625, 0.5), (1.0, 0.5)]

curve模块 - 贝塞尔曲线

curve模块包含用于处理贝塞尔曲线的常见基础功能。

Curve(nodes, degree, copy=True, verify=True)

代表一条贝塞尔曲线。

参数:

>>> import bezier

>>> # 二维贝塞尔曲线
>>> nodes1 = [[0.0, 0.625, 1.0], [0.0, 0.5, 0.5]]
>>> curve1 = bezier.Curve(nodes, 2)
>>> curve1
<Curve (degree=2, dimension=2)>

>>> # 阶数错误,因为verify=False所以不报错
>>> curve2 = bezier.Curve(nodes, 2, verify=False)
>>> curve2
<Curve (degree=2, dimension=2)>

>>> # 三维贝塞尔曲线
>>> nodes2 = [[0.0, 0.625, 1.0], [0.0, 0.5, 0.5]]
>>> curve3 = bezier.Curve(nodes, 2)
>>> curve3
<Curve (degree=2, dimension=2)>
Curve()代码示例配图

方法

Curve.from_nodes(nodes, copy=True)

通过节点创建一条贝塞尔曲线,无需指定阶数。

参数:

>>> import bezier
>>> nodes = [[0.0, 0.625, 1.0], [0.0, 0.5, 0.5]]
>>> curve = bezier.Curve.from_nodes(nodes)
>>> curve
<Curve (degree=2, dimension=2)>

Curve.copy() -> Curve

复制一条贝塞尔曲线。

>>> import bezier
>>> nodes = [[0.0, 0.625, 1.0], [0.0, 0.5, 0.5]]
>>> curve = bezier.Curve.from_nodes(nodes)
>>> curve
<Curve (degree=2, dimension=2)>
>>> copy = curve.copy()
>>> copy
<Curve (degree=2, dimension=2)>

Curve.evaluate(s) -> numpy.ndarray

求出曲线上参数为s的点的坐标。

参数:

>>> import bezier
>>> nodes = [[0.0, 0.625, 1.0], [0.0, 0.5, 0.5]]
>>> curve = bezier.Curve(nodes, 2)
>>> point = curve.evaluate(0.5)
>>> point
array([[0.5625],
       [0.375 ]])
Curve.evaluate()代码示例配图

Curve.evaluate_multi(s_vals) -> numpy.ndarray

求出曲线上的s_vals内多个参数的点的坐标,返回值格式为[[x1, x2, ..., xn], [y, y2, ..., yn], [z1, z2, ..., zn]]

参数:

Curve.evaluate_multi()代码示例配图
>>> import bezier
>>> import numpy as np
>>> nodes = [[0.0, 0.625, 1.0], [0.0, 0.5, 0.5]]
>>> curve = bezier.Curve.from_nodes(nodes)
>>> s_vals = np.linspace(0.0, 1.0, 5)
>>> points = curve.evaluate_multi(s_vals)
>>> points
array([[0.      , 0.296875, 0.5625  , 0.796875, 1.      ],
       [0.      , 0.21875 , 0.375   , 0.46875 , 0.5     ]])

Curve.evaluate_hodograph(s) -> numpy.ndarray

求出曲线上参数为s的点的切向量,即\((x_{起点}-x_{终点},y_{起点}-y_{终点})\)。

参数:

>>> import bezier
>>> nodes = [[0.0, 0.625, 1.0], [0.0, 0.5, 0.5]]
>>> curve = bezier.Curve(nodes, 2)
>>> vector = curve.evaluate_hodograph(0.5)
>>> vector
array([[1. ],
       [0.5]])
Curve.hodograph()代码示例配图

Curve.plot(num_pts, color=None, alpha=None, ax=None) -> matplotlib.artist.Artist

将曲线绘制到matplotlib图表上。曲线必须是二维,否则会触发NotImplementedError。

此方法需要matplotlib库。

参数:

>>> import bezier
>>> import matplotlib.pyplot as plt
>>> nodes = [[0.0, 0.625, 1.0], [0.0, 0.5, 0.5]]
>>> curve = bezier.Curve(nodes, 2)
>>> x, y = curve.evaluate(0.5)

>>> # 直接用plt快速画一条曲线
>>> curve.plot(100, color='red', alpha=0.5)
<Axes: >
>>> plt.plot(x, y, marker='o')
[<matplotlib.lines.LineD object at (内存地址)>]
>>> plt.show()
Curve.plot()代码示例配图1

>>> # 单独操作axes,把多条曲线画在同一图表上
>>> nodes1 = [[0.0, 0.625, 1.0], [0.0, 0.5, 0.5]]
>>> curve1 = bezier.Curve(nodes1, 2)
>>> nodes2 = [[0.6, 0.7, 0.6], [0.0, 0.5, 1.0]]
>>> curve2 = bezier.Curve(nodes2, 2)
>>> fig, ax = plt.subplots()
>>> curve1.plot(100, ax=ax)
<Axes: >
>>> curve2.plot(100, ax=ax)
<Axes: >
>>> ax.plot(x, y, marker='o')
[<matplotlib.lines.LineD object at (内存地址)>]
>>> plt.show()
Curve.plot()代码示例配图2

Curve.subdivide() -> tuple Curve

将曲线从正中间分为两部分。

>>> import bezier
>>> nodes = [[0.0, 0.625, 1.0], [0.0, 0.5, 0.5]]
>>> curve = bezier.Curve(nodes, 2)
>>> left, right = curve.subdivide()
>>> left, right
<Curve (degree=2, dimension=2)> <Curve (degree=2, dimension=2)>
Curve.subdivide()代码示例配图

Curve.intersect(other, strategy=IntersectionStrategy.GEOMETRIC, verify=True) -> numpy.ndarray

求出两条曲线的交点在两条曲线上的参数值,如果没有交点则返回值为空。当verify=True且两条线不都是二维曲线时,会触发NotImplementedError。

参数:

>>> import bezier
>>> import numpy as np
>>> nodes1 = [[0.0, 0.625, 1.0], [0.0, 0.5, 0.5]]
>>> curve1 = bezier.Curve(nodes1, 2)
>>> nodes2 = [[0.6, 0.7, 0.6], [0.0, 0.5, 1.0]]
>>> curve2 = bezier.Curve(nodes2, 2)
>>> s_vals = curve1.intersect(curve2)
>>> s_vals
array([[0.58799506],
        [0.41512597]])
>>> s_vals = np.array(s_vals[0])
>>> intersections = list(zip(*curve1.evaluate_multi(s_vals)))
>>> intersections
[(np.float64(0.6485592796603902), np.float64(0.41512596570181837))]
Curve.intersect()代码示例配图

Curve.self_intersections(strategy=IntersectionStrategy.GEOMETRIC, verify=True) -> numpy.ndarray

求出一条曲线的自我相交点在曲线上的参数值,如果没有自我相交点则返回值为空。

参数:

>>> import bezier
>>> import numpy as np
>>> nodes = [[-300.0, 227.5, -730.0, 0.0, 730.0, -227.5, 300.0], [150.0, 953.75, -2848.0, 4404.75, -2848.0, 953.75, 150.0]]
>>> curve = bezier.Curve(nodes, 6)
>>> s_vals = curve.self_intersections()
>>> s_vals
array([[0.16666667, 0.66666667],
        [0.33333333, 0.83333333]])
>>> s_vals =  np.array(s_vals[0])
>>> intersections = list(zip(*curve.evaluate_multi(s_vals)))
>>> intersections
[(np.float64(-149.99999999999997), np.float64(74.99999999999989)), (np.float64(150.0), np.float64(74.99999999999991))]
Curve.self_intersections()代码示例配图

Curve.elevate() -> Curve

在保持曲线不变形的情况下将曲线升高一阶。

>>> import bezier
>>> nodes = [[0.0, 1.5, 3.0], [0.0, 1.5, 0.0]]
>>> curve = bezier.Curve(nodes, 2)
>>> curve                     
<Curve (degree=2, dimension=2)>
>>> elevated = curve.elevate()
>>> elevated
<Curve (degree=3, dimension=2)>
Curve.elevate()代码示例配图

Curve.reduce_() -> Curve

将曲线降低一阶,不是所有曲线都能在降阶后保持不变形。

>>> import bezier

>>> # 降阶后不变形
>>> nodes = [[-3.0, 0.0, 1.0, 0.0], [3.0, 2.0, 3.0, 6.0]]
>>> curve = bezier.Curve(nodes, 3)
>>> curve
<Curve (degree=3, dimension=2)>
>>> reduced = curve.reduce_()
>>> reduced
<Curve (degree=2, dimension=2)>

>>> # 降阶后变形
>>> nodes = [[0.0, 1.5, 3.0], [0.0, 1.5, 0.0]]
>>> curve = bezier.Curve(nodes, 2)
>>> curve
<Curve (degree=2, dimension=2)>
>>> reduced = curve.reduce_()
>>> reduced
<Curve (degree=1, dimension=2)>
Curve.reduce_()曲线不变形代码示例配图 Curve.reduce_()曲线变形代码示例配图

Curve.specialize(start, end) -> Curve

截取曲线上参数在start与end间的部分。

参数:

>>> import bezier
>>> nodes = [[-3.0, 0.0, 1.0, 0.0], [3.0, 2.0, 3.0, 6.0]]
>>> curve = bezier.Curve(nodes, 3)
>>> curve
<Curve (degree=3, dimension=2)>
>>> slice = curve.specialize(0.2, 0.7)
>>> slice
<Curve (degree=3, dimension=2)>
Curve.specialize()代码示例配图

Curve.locate(point) -> Optional float

求出曲线上某一点的参数,如果点不在曲线上则返回值为空。

参数:

>>> import bezier
>>> import numpy as np
>>> nodes = [[-3.0, 0.0, 1.0, 0.0], [3.0, 2.0, 3.0, 6.0]]
>>> curve = bezier.Curve(nodes, 3)
>>> point = np.array([[-0.84], [2.64]])
>>> s = curve.locate(point)
>>> s # 计算结果会有浮点数误差
0.29999999999992016
Curve.locate()代码示例配图

Curve.to_symbolic() -> sympy.Matrix

求出曲线的参数方程,返回矩阵\(\begin{bmatrix} x\\y\end{bmatrix}\)。

此方法需要SymPy库。

>>> import bezier
>>> import sympy
>>> nodes = [[0.0, 0.625, 1.0], [0.0, 0.5, 0.5]]
>>> curve = bezier.Curve(nodes, 2)
>>> matrix = curve.to_symbolic()
>>> matrix
Matrix([
[-s*(s - 5)/4],
[-s*(s - 2)/2]])
Curve.to_symbolic()代码示例配图

Curve.implicitize() -> sympy.Expr

求出表示曲线的函数。曲线必须是二维,否则会触发NotImplementedError。

此方法需要SymPy库。

>>> import bezier
>>> nodes = [[-3.0, 0.0, 1.0, 0.0], [3.0, 2.0, 3.0, 6.0]]
>>> curve = bezier.Curve(nodes, 3)
>>> f = curve.implicitize()
>>> f
36*(x**2 + 2*x*y - 3*x + y**2 - 9*y + 18)
Curve.implicitize()代码示例配图

属性

Curve.length -> float

曲线的长度。

>>> import bezier
>>> nodes = [[0.0, 0.625, 1.0], [0.0, 0.5, 0.5]]
>>> curve = bezier.Curve(nodes, 2)
>>> length = curve.length
>>> length
1.1362104785667901

Curve.degree -> int

曲线的阶数。

>>> import bezier
>>> nodes = [[0.0, 0.625, 1.0], [0.0, 0.5, 0.5]]
>>> curve = bezier.Curve(nodes, 2)
>>> degree = curve.degree
>>> degree
2

Curve.dimension -> int

曲线的维度。

>>> import bezier
>>> nodes = [[0.0, 0.625, 1.0], [0.0, 0.5, 0.5]]
>>> curve = bezier.Curve(nodes, 2)
>>> dimension = curve.dimension
>>> dimension
2

Curve.nodes -> numpy.ndarray

曲线的节点。

>>> import bezier
>>> nodes = [[0.0, 0.625, 1.0], [0.0, 0.5, 0.5]]
>>> curve = bezier.Curve(nodes, 2)
>>> nodes = curve.nodes
>>> nodes
array([[0.   , 0.625, 1.   ],
       [0.   , 0.5  , 0.5  ]])

curved_polygon模块 - 贝塞尔曲线边多边形

curve_polygon模块可以帮我们处理边是贝塞尔曲线的多边形。

CurvedPolygon(*edges, **kwargs)

代表一条边是贝塞尔曲线的多边形。

参数:

>>> import bezier
>>> edges = [
...     [[0.0, 1.0, 2.0], [0.0, -1.0, 0.0]],
...     [[2.0, 2.0], [0.0, 1.0]],
...     [[2.0, 1.0, 0.0], [1.0, 2.0, 1.0]],
...     [[0.0, 0.0], [1.0, 0.0]]
... ]
>>> edge1, edge2, edge3, edge4 = list(map(lambda edge: bezier.Curve.from_nodes(edge), edges))
>>> polygon = bezier.CurvedPolygon(edge1, edge2, edge3, edge4)
>>> polygon
<CurvedPolygon (num_sides=4)>
CurvedPolygon()代码示例配图

方法

CurvedPolygon.plot(pts_per_edge, color=None, ax=None, alpha=0.625) -> matplotlib.artist.Artist

将多边形绘制到matplotlib图表上。多边形必须是二维,否则会触发NotImplementedError。

此方法需要matplotlib库。

参数:

>>> import bezier
>>> import matplotlib.pyplot as plt
>>> edges1 = [
...     [[0.0, 1.0, 2.0], [0.0, -1.0, 0.0]],
...     [[2.0, 2.0], [0.0, 1.0]],
...     [[2.0, 1.0, 0.0], [1.0, 2.0, 1.0]],
...     [[0.0, 0.0], [1.0, 0.0]]
... ]
>>> edges2 = [
...     [[0.0, 1.0], [0.0, 0.0]],
...     [[1.0, 1.25, 1.0], [0.0, 0.5, 1.0]],
...     [[1.0, 2.0], [1.0, 1.0]],
...     [[2.0, 1.0, 0.0], [1.0, 0.75, 0.0]]
... ]
>>> edge1, edge2, edge3, edge4 = list(map(lambda edge: bezier.Curve.from_nodes(edge), edges1))
>>> edge5, edge6, edge7, edge8 = list(map(lambda edge: bezier.Curve.from_nodes(edge), edges2))
>>> polygon1 = bezier.CurvedPolygon(edge1, edge2, edge3, edge4)
>>> polygon2 = bezier.CurvedPolygon(edge5, edge6, edge7, edge8)
>>> fig, ax = plt.subplots()
>>> polygon1.plot(50, ax=ax, color=(0, 0, 0), alpha=0.8)
<Axes: >
>>> polygon2.plot(50, ax=ax)
<Axes: >
>>> plt.show()
CurvedPolygon.plot()代码示例配图

属性

CurvedPolygon.num_sides -> int

多边形的边数。

>>> import bezier
>>> edges = [
...     [[0.0, 1.0, 2.0], [0.0, -1.0, 0.0]],
...     [[2.0, 2.0], [0.0, 1.0]],
...     [[2.0, 1.0, 0.0], [1.0, 2.0, 1.0]],
...     [[0.0, 0.0], [1.0, 0.0]]
... ]
>>> edge1, edge2, edge3, edge4 = list(map(lambda edge: bezier.Curve.from_nodes(edge), edges))
>>> polygon = bezier.CurvedPolygon(edge1, edge2, edge3, edge4)
>>> num_sides = polygon.num_sides
>>> num_sides
4

CurvedPolygon.area -> float

多边形的面积。

>>> import bezier
>>> edges = [
...     [[0.0, 1.0, 2.0], [0.0, -1.0, 0.0]],
...     [[2.0, 2.0], [0.0, 1.0]],
...     [[2.0, 1.0, 0.0], [1.0, 2.0, 1.0]],
...     [[0.0, 0.0], [1.0, 0.0]]
... ]
>>> edge1, edge2, edge3, edge4 = list(map(lambda edge: bezier.Curve.from_nodes(edge), edges))
>>> polygon = bezier.CurvedPolygon(edge1, edge2, edge3, edge4)
>>> area = polygon.area
>>> area
3.333333333333333

triangle模块 - 贝塞尔曲线边三角形

triangle模块可以帮我们处理贝塞尔曲线边的三角形。

Triangle(nodes, degree, copy=True, verify=True)

代表一条贝塞尔曲线边三角形。设阶数为\(d\),则节点为\(N_1,N_2,N_3,\cdots ,N_{3d}\),其中\(N_1,N_{d+1},N_{2d+1}\)为三角形的角。

参数:

>>> import bezier
>>> nodes = [[0.0, 0.5, 1.0, 0.125, 0.375, 0.25], [0.0, 0.0, 0.25, 0.5, 0.375, 1.0]]
>>> triangle = bezier.Triangle(nodes, 2)
>>> triangle
<Triangle (degree=2, dimension=2)>
Triangle()代码示例配图

方法

以下这几个方法已经超出我能看懂的范畴了,自己去官方文档看吧:

Triangle.from_nodes(nodes, copy=True)

通过节点创建一条贝塞尔曲线边三角形,无需指定阶数。

参数:

>>> import bezier
>>> nodes = [[0.0, 0.5, 1.0, 0.125, 0.375, 0.25], [0.0, 0.0, 0.25, 0.5, 0.375, 1.0]]
>>> triangle = bezier.Triangle.from_nodes(nodes)
>>> triangle
<Triangle (degree=2, dimension=2)>

Triangle.plot(pts_per_edge, color=None, ax=None, with_nodes=False, alpha=0.625) -> matplotlib.artist.Artist

将三角形绘制到matplotlib图表上。三角形必须是二维,否则会触发NotImplementedError。

此方法需要matplotlib库。

参数:

>>> import bezier
>>> import matplotlib.pyplot as plt
>>> nodes1 = [[0.0, 0.5, 1.0, 0.125, 0.375, 0.25], [0.0, 0.0, 0.25, 0.5, 0.375, 1.0]]
>>> triangle1 = bezier.Triangle(nodes1, 2)
>>> nodes2 = [[0.0, 1.0, 2.0, -1.5, -0.5, -3.0], [0.0, 0.75, 1.0, 1.0, 1.5, 2.0]]
>>> triangle2 = bezier.Triangle(nodes2, 2)
>>> fig, ax = plt.subplots()
>>> triangle1.plot(50, ax=ax, alpha=0.8)
<Axes: >
>>> triangle2.plot(50, ax=ax, with_nodes=True)
<Axes: >
>>> plt.show()
Triangle.plot()代码示例配图

Triangle.subdivide() -> tuple Triangle,Triangle,Triangle,Triangle

将三角形分为四个小三角形。设三角形的三个节点是\(N_1,N_2,N_3\),共三条曲线\(N_1N_2,N_2N_3,N_1N_3\),三条曲线上参数为0.5的点分别为\(M_1,M_2,M_3\),四个小三角形就是\(\triangle N_1M_1M_3,\triangle N_2M_1M_2,\triangle N_3M_2M_3,\triangle M_1M_2M_3\)。

>>> import bezier
>>> nodes = [[0.0, 0.5, 1.0, 0.125, 0.375, 0.25], [0.0, 0.0, 0.25, 0.5, 0.375, 1.0]]
>>> triangle = bezier.Triangle(nodes, 2)
>>> triangles = triangle.subdivide()
>>> triangles
(<Triangle (degree=2, dimension=2)>, <Triangle (degree=2, dimension=2)>, <Triangle (degree=2, dimension=2)>, <Triangle (degree=2, dimension=2)>)
Triangle.subdivide()代码示例配图

Triangle.intersect(other, strategy=IntersectionStrategy.GEOMETRIC, verify=True) -> list Union CurvedPolygon,Triangle

求出两个三角形的相交区域。

参数:

>>> import bezier

>>> nodes = [[0.0, 0.5, 1.0, 0.125, 0.375, 0.25], [0.0, 0.0, 0.25, 0.5, 0.375, 1.0]]
>>> triangle = bezier.Triangle(nodes, 2)
>>> nodes = [[0.0, 1.0, 2.0, -1.5., -0.5., -3.0.], [0.0, 0.75, 1.0, 1.0, 1.5, 2.0]]
>>> triangle = bezier.Triangle(nodes, 2)
>>> intersection = triangle.intersect(triangle)  
>>> intersection
[<CurvedPolygon (num_sides=3)>]
Triangle.intersect()代码示例配图

Triangle.elevate() -> Triangle

在保持三角形不变形的情况下将曲线的每条边升高一阶。

>>> import bezier
>>> nodes = [[0.0, 0.5, 1.0, 0.125, 0.375, 0.25], [0.0, 0.0, 0.25, 0.5, 0.375, 1.0]]
>>> triangle = bezier.Triangle(nodes, 2)
>>> triangle
<Triangle (degree=2, dimension=2)>
>>> elevated = triangle.elevate()
>>> elevated
<Triangle (degree=3, dimension=2)>
Triangle.elevate()代码示例配图

属性

Triangle.area -> float

三角形的面积。

>>> import bezier
>>> nodes = [[0.0, 0.5, 1.0, 0.125, 0.375, 0.25], [0.0, 0.0, 0.25, 0.5, 0.375, 1.0]]
>>> triangle = bezier.Triangle(nodes, 2)
>>> area = triangle.area
>>> area
0.3854166666666667

Triangle.edges -> tuple Curve,Curve,Curve

三角形的三边。

>>> import bezier
>>> nodes = [[0.0, 0.5, 1.0, 0.125, 0.375, 0.25], [0.0, 0.0, 0.25, 0.5, 0.375, 1.0]]
>>> triangle = bezier.Triangle(nodes, 2)
>>> edges = triangle.edges
>>> edges
(<Curve (degree=2, dimension=2)>, <Curve (degree=2, dimension=2)>, <Curve (degree=2, dimension=2)>)

Triangle.is_valid -> bool

作者搞不懂,自己去官方文档看吧。

Curve.degree -> int

三角形每边的阶数。

>>> import bezier
>>> nodes = [[0.0, 0.5, 1.0, 0.125, 0.375, 0.25], [0.0, 0.0, 0.25, 0.5, 0.375, 1.0]]
>>> triangle = bezier.Triangle(nodes, 2)
>>> degree = triangle.degree
>>> degree
2

Curve.dimension -> int

三角形的维度。

>>> import bezier
>>> nodes = [[0.0, 0.5, 1.0, 0.125, 0.375, 0.25], [0.0, 0.0, 0.25, 0.5, 0.375, 1.0]]
>>> triangle = bezier.Triangle(nodes, 2)
>>> dimension = triangle.dimension
>>> dimension
2

Curve.nodes -> numpy.ndarray

三角形的节点。

>>> import bezier
>>> nodes = [[0.0, 0.5, 1.0, 0.125, 0.375, 0.25], [0.0, 0.0, 0.25, 0.5, 0.375, 1.0]]
>>> triangle = bezier.Triangle(nodes, 2)
>>> nodes = triangle.nodes
>>> nodes
array([[0.   , 0.5  , 1.   , 0.125, 0.375, 0.25 ],
       [0.   , 0.   , 0.25 , 0.5  , 0.375, 1.   ]])