杭州电子科技大学数字几何与仿真处理作业——绘制贝塞尔曲线

贝塞尔曲线就不介绍了,如果不会的话可以看看别人的文章,或者好好听许老师上课讲的课。主要就是使用 伯恩斯坦多项式 根据给定的控制顶点绘制曲线。

控制顶点(其实就是普通的点)定义如下。

class Point():
    def __init__(self, x: int, y: int):
        self.x = x
        self.y = y

贝塞尔曲线定义如下

首先贝塞尔曲线需要多个控制顶点,因此使用self.points列表存储贝塞尔曲线的顶点

add_point和delete_point定义了贝塞尔曲线删除和增加顶点的方法(贝塞尔曲线至少要有两个控制顶点)

fun函数则计算了当t = u时,B(t)计算出的x,y坐标。

from point import Point
import math

class BezierCurve():
    def __init__(self, points: list):
        self.points = points
        self.length = len(points)

    def add_point(self, x, y):
        t = Point(x, y)
        self.points.append(t)
        self.length = self.length + 1

    def delete_point(self):
        if self.length > 2:
            self.points.pop()
            self.length = self.length - 1

    def fun(self, u: float):
        # 定义组合数计算函数
        def com(k: int, n: int):
            return math.factorial(n) // (math.factorial(k) * math.factorial(n - k))
        ans_x = 0
        # print(ans_x)
        for i in range(0, self.length):
            # print(ans_x, "round", i)
            ans_x = (ans_x + self.points[i].x *
                     com(i, self.length - 1) * (u ** i) * ((1 - u) ** (self.length - 1 - i)))

        ans_y = 0
        for i in range(0, self.length):
            # print(ans_y)
            ans_y = (ans_y + self.points[i].y *
                     com(i, self.length - 1) * (u ** i) * ((1 - u) ** (self.length -1 - i)))

        return ans_x, ans_y

绘画模块定义如下

mouseMoveEvent根据鼠标和贝塞尔曲线顶点的距离判断鼠标点击时选择的是哪一个顶点。drawpoints函数绘制贝塞尔曲线上的点,根据这些点组成了曲线。(其实就是调用了上文贝塞尔曲线中定义的fun函数1000次,获得了当t=0,0.001,0.002,0.003…………0.999,1的值,并绘制出这些点。因为点足够密集就形成了曲线。

import sys,math
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from bezierCurve import BezierCurve
from point import Point

class Drawing(QWidget):
    def __init__(self, parent=None):
        super(Drawing, self).__init__(parent)
        self.resize(700, 700)
        self.setWindowTitle('绘制三阶贝塞尔曲线')
        # 初始化贝塞尔曲线中的三个点
        point1 = Point(100, 400)
        point2 = Point(200, 200)
        point3 = Point(500, 300)
        points = [point1, point2, point3]
        # 初始化贝塞尔曲线
        self.curve = BezierCurve(points)
        # 鼠标选中的点序号
        self.n = 1

    # def mousePressEvent(self, event):
    #     def lenth_of_two_points_nosqrt(x1, y1, x2, y2):
    #         return (x1 - x2) ** 2 + (y1 - y2) ** 2
    #     x = event.x
    #     y = event.y
    #     # 鼠标点击可以离点的最大距离的平方
    #     length = 100
    #     if lenth_of_two_points_nosqrt(x, y, self.curve.x1, self.curve.y1) < length:
    #         self.mouseMoveEvent(event)
    #         self.n = 1
    #     if lenth_of_two_points_nosqrt(x, y, self.curve.x2, self.curve.y2) < length:
    #         self.mouseMoveEvent(event)
    #         self.n = 2
    #     if lenth_of_two_points_nosqrt(x, y, self.curve.x3, self.curve.y3) < length:
    #         self.mouseMoveEvent(event)
    #         self.n = 3
    #     if lenth_of_two_points_nosqrt(x, y, self.curve.x4, self.curve.y4) < length:
    #         self.mouseMoveEvent(event)
    #         self.n = 4

    def mouseMoveEvent(self, event):
        def lenth_of_two_points_nosqrt(x1, y1, x2, y2):
            # print(type(x1), type(y1), type(x2), type(y2))
            return (x1 - x2) ** 2 + (y1 - y2) ** 2

        x = event.x()
        y = event.y()
        print(x, y)
        # 鼠标点击可以离点的最大距离的平方
        length = 100
        no_choose_flag = True
        for i in range(0, self.curve.length):
            print()
            print("当是i=", i, "时,距离等于", lenth_of_two_points_nosqrt(x, y, self.curve.points[i].x, self.curve.points[i].y))
            if lenth_of_two_points_nosqrt(x, y, self.curve.points[i].x, self.curve.points[i].y) < length:
                # no_choose_flag = False
                self.n = i
        print(self.n)
        # 由于cpu性能问题,如果设置无法选取会导致刷新速度跟不上鼠标移动速度,
        # 最终超出鼠标离开点的最大距离,不再更新
        # if no_choose_flag:
            # return
        x = event.x()
        y = event.y()
        # print(x, y)
        for i in range(0, self.curve.length):
            if(i == self.n):
                self.curve.points[self.n].x = x
                self.curve.points[self.n].y = y
                self.paintEvent(event)
        self.update()




    def paintEvent(self, event):
        qp = QPainter()
        qp.begin(self)
        self.drawPoints(qp)
        qp.end()

    def drawPoints(self, qp):
        size = self.size()

        # 设置绘制笔的大小
        pen = QPen(Qt.blue)
        pen.setWidth(10)
        qp.setPen(pen)
        # 绘制控制顶点
        for i in range(0, self.curve.length):
            # print(self.curve.points[i].x, self.curve.points[i].y)
            qp.drawPoint(self.curve.points[i].x, self.curve.points[i].y)

        # 设置第2条直线的画笔
        pen.setColor(Qt.black)  # 设置画笔颜色为蓝色
        pen.setStyle(Qt.DashLine)  # 设置画笔样式为由一些像素分割的短线
        pen.setWidth(2)  # 设置画笔宽度
        qp.setPen(pen)  # 设置画笔
        for i in range(0, self.curve.length-1):
            # 绘制直线
            # print(self.curve.points[i].x, self.curve.points[i].y,
            #            self.curve.points[i+1].x, self.curve.points[i+1].y)
            qp.drawLine(self.curve.points[i].x, self.curve.points[i].y,
                        self.curve.points[i+1].x, self.curve.points[i+1].y)

        # 设置绘制笔的大小
        pen = QPen(Qt.red)
        pen.setWidth(2)
        qp.setPen(pen)

        for i in range(1000):

            x, y = self.curve.fun(i/1000)
            # print(x, y)
            qp.drawPoint(x, y)

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    drawing = Drawing()
    drawing.show()
    sys.exit(app.exec_())

主函数定义如下

增加了增加点和减少点的按钮,并调用了相应函数重新绘制。

import sys,math
from point import Point
from drawing_new import Drawing
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from ThreeOrderedBezierCurve import ThreeOrderedBezierCurve

class Stats():
    def __init__(self):
        self.window = QMainWindow()
        self.window.resize(900, 900)
        self.window.move(10, 10)
        self.window.setWindowTitle('绘制贝塞尔曲线')

        self.drawing = Drawing(self.window)
        self.drawing.move(0, 0)
        self.drawing.resize(900, 700)
        # 增加一个点
        self.button1 = QPushButton(self.window)
        self.button1.setText("添加一个点")
        self.button1.move(150, 700)
        self.button1.clicked.connect(self.add_point)
        # 减少一个点
        self.button2 = QPushButton(self.window)
        self.button2.setText("减少一个点")
        self.button2.move(600, 700)
        self.button2.clicked.connect(self.delete_point)

        # # 增加一个点
        # self.label1 = QLabel(self.window)
        # pixmap = QPixmap('path_to_your_image.png')  # 替换为你的图片路径
        # self.label1.setPixmap(pixmap)
        # self.label1.setAlignment(Qt.AlignCenter)
        # self.label1.setScaledContents(True)
        # self.label1.mousePressEvent = self.add_point



    def add_point(self):
        self.drawing.curve.add_point(self.drawing.curve.points[-1].x + 10, self.drawing.curve.points[-1].y + 10)
        self.drawing.update()

    def delete_point(self):
        self.drawing.curve.delete_point()
        self.drawing.update()



if __name__ == '__main__':
    app = QApplication([])
    stats = Stats()
    stats.window.show()
    app.exec_()

最终效果如下

总结:

贝塞尔曲线原理还是蛮简单的,但是做这个作业需要掌握一种能将曲线显示出来的技术(web,qt,pyqt之类的)希望我的文章能帮助到不是很了解这些技术的同学。

ps:我用的pyqt

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值