[python]科学绘图-贝塞尔曲线(Bezier curve)的绘制

1、贝塞尔曲线两种绘制方式

贝塞尔曲线按照绘制方式有两种:

一种是直接绘制三个控制点A、B、C,

        直线AB的方程式:f(t) = A·(1-t) + B·t, (0 <= t <=1)

        直线BC的方程式:g(t) = B·(1-t) + C·t, (0 <= t <=1)

        曲线AC的方程式:h(t) = f(t)·(1-t) + g(t)·t, (0 <= t <=1)

 另一种是绘制两端+两个控制线(两个手柄)

但是其实,两个手柄就是控制点,加上首尾就是4个控制点

2、python代码展示

需要安装第三方库matplotlib:

pip install matplotlib

 完整代码如下:

import math
import matplotlib
import matplotlib.pyplot as plt
matplotlib.use('TkAgg')


class Bezier:
    def __init__(self, points: [[]]):
        self.points = points
        self.n = len(points) - 1

    def curve(self, t: float) -> [float]:
        points, n = self.points, self.n
        result = list(sum(math.comb(n, i) * (1 - t) ** (n - i) * t ** i * points[i][j] for i in range(n + 1)) for j in
                      range(len(points[0])))
        return result

    def appendPoint(self, point: [float]):
        self.points.append(point)
        self.n += 1

    def _rang(self, quantity: int = 100) -> [float]:
        return [i / quantity for i in range(quantity + 1)]

    def getCoordinate(self, quantity: int = 100) -> [[]]:
        return [self.curve(i) for i in self._rang(quantity)]

    def draw(self, ax: plt.Axes):
        if len(self.points[0]) != 2:
            raise ValueError('仅支持二维')
        xs, ys = list(zip(*self.getCoordinate()))
        ax.plot(xs, ys, '-', color='red', label='bezier curve')
        ax.plot(*list(zip(*ps)), '-.', color='green')
        ax.scatter(*list(zip(*ps)), color='blue', label='control points')
        ax.grid()
        ax.set_aspect("equal")
        ax.legend()


if __name__ == '__main__':
    fig, axs = plt.subplots()

    ps = [[0,0],[0,1],[1,0],[1,1]]
    bz = Bezier(ps)
    bz.draw(axs)
    plt.show()

运行结果:

 

图像中:

        蓝色点是控制点

        绿色线为了表示点的顺序

        红色是贝塞尔曲线

3、代码讲解

创建Bezier贝塞尔曲线class,构造器__init__(points)参数为点集,例如代码中点集为ps = [[0,0],[0,1],[1,0],[1,1]]

def curve(t)中,t为参数,范围为[0,1],用于计算贝塞尔曲线的方程式

内置def draw(),用于在matplotlib中展示结果,其中quantity为绘制曲线断点的数量,数越高,曲线越平滑

def appendPoint(point),末尾增加一个控制点,由此你可以扩展出更多类似方法

4、案例修改

比如你想要用贝塞尔曲线绘制一个圆形(实际上不是的,只能是看着像圆)

将ps行改为ps = [[0, 0], [1, 0], [1, 1.8], [-1, 1.8], [-1, 0], [0, 0]]

就能得到以下结果:

5、拓展多段三阶贝塞尔曲线

绘制一个圆形,这通常是一个PS使用者需要练习钢笔工具使用的,可以看到,这里就是四段三阶贝塞尔曲线(三阶贝塞尔曲线就是四个控制点)

代码如下:

import math
import matplotlib
import matplotlib.pyplot as plt
matplotlib.use('TkAgg')


class SubBezier:
    def __init__(self, pointss: [[[]]]):
        self.pointss = pointss
        self.ns = list(len(points) - 1 for points in self.pointss)
        self.colors = ['red', 'green', 'blue', 'orange', 'brown', 'yellow', 'purple', 'pink', ]

    def curve(self, t: float) -> [float]:
        points, ns = self.pointss, self.ns
        results = list(
            list(
                sum(math.comb(ns[k], i) * (1 - t) ** (ns[k] - i) * t ** i * points[k][i][j]
                    for i in range(ns[k] + 1)) for j in range(len(points[k][0]))
            )
            for k in range(len(ns)))
        return results

    def _rang(self, quantity: int = 100) -> [float]:
        return [i / quantity for i in range(quantity + 1)]

    def getCoordinate(self, quantity: int = 100) -> [[]]:
        print([self.curve(i) for i in self._rang(quantity)])
        return [self.curve(i) for i in self._rang(quantity)]

    def draw(self, ax: plt.Axes):
        for index, s in enumerate(list(zip(*self.getCoordinate()))):
            xs, ys = list(zip(*s))
            ax.plot(xs, ys, color=self.colors[index])
            ax.scatter(*list(zip(*ps[index])), color=self.colors[index])
            ax.plot(*list(zip(*ps[index])), color=self.colors[index])
        ax.grid()
        ax.set_aspect("equal")


if __name__ == '__main__':
    fig, axs = plt.subplots()

    ps = [
        [[0, 1], [0, 0.4], [0.4, 0], [1, 0]],
        [[1, 0], [1.6, 0], [2, 0.4], [2, 1]],
        [[2, 1], [2, 1.6], [1.6, 2], [1, 2]],
        [[1, 2], [0.4, 2], [0, 1.6], [0, 1]]
    ]
    bz = SubBezier(ps)
    bz.draw(axs)
    plt.show()

感谢您的观看,如果对你有收获,不妨点个赞

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值