贝塞尔曲线(自动生成控制点)
效果
代码:
void CurveLearn::paintEvent(QPaintEvent *) {
// 曲线上的点
static QList<QPointF> points = QList<QPointF>() << QPointF(0, 0) << QPointF(100, 100) << QPointF(200, -100)
<< QPointF(300, 100) << QPointF(330, -80) << QPointF(350, -70);
QPainterPath path(points[0]);
for (int i = 0; i < points.size() - 1; ++i) {
// 控制点的 x 坐标为 sp 与 ep 的 x 坐标和的一半
// 第一个控制点 c1 的 y 坐标为起始点 sp 的 y 坐标
// 第二个控制点 c2 的 y 坐标为结束点 ep 的 y 坐标
QPointF sp = points[i];
QPointF ep = points[i+1];
QPointF c1 = QPointF((sp.x() + ep.x()) / 2, sp.y());
QPointF c2 = QPointF((sp.x() + ep.x()) / 2, ep.y());
// QPointF c2 = QPointF(ep.x(), (sp.y() + ep.y()) / 2);
path.cubicTo(c1, c2, ep);
}
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setPen(QPen(Qt::black, 2));
// 绘制 path
//移动默认(0,0)到(40,130)
painter.translate(40, 130);
painter.drawPath(path);
// 绘制曲线上的点
painter.setBrush(Qt::gray);
for (int i = 0; i < points.size(); ++i) {
painter.drawEllipse(points[i], 4, 4);
}
}
2.另一种效果
资源路径:https://download.csdn.net/download/yuxing55555/10841947
pyqt中绘制贝塞尔曲线(示例)
from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsScene, QGraphicsView, QWidget, QLabel
from PyQt5.QtGui import QPainterPath, QPainter, QPen
from PyQt5.QtCore import Qt
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.label_size = 5
self.resize(600, 600)
self.startLabel = QLabel(self)
self.startLabel.resize(self.label_size, self.label_size)
self.startLabel.setStyleSheet('QLabel{background-color:red}')
self.startLabel.move(100, 110)
self.ctrlLabel1 = QLabel(self)
self.ctrlLabel1.resize(self.label_size, self.label_size)
self.ctrlLabel1.setStyleSheet('QLabel{background-color:green}')
self.ctrlLabel1.move(200, 100)
self.ctrlLabel2 = QLabel(self)
self.ctrlLabel2.resize(self.label_size, self.label_size)
self.ctrlLabel2.setStyleSheet('QLabel{background-color:green}')
self.ctrlLabel2.move(300, 300)
self.endLabel = QLabel(self)
self.endLabel.resize(self.label_size, self.label_size)
self.endLabel.setStyleSheet('QLabel{background-color:red}')
self.endLabel.move(400, 200)
self.selectedWidget = None
def paintEvent(self, event):
pen = QPen(Qt.black, 2)
painter = QPainter()
painter.begin(self)
painter.setPen(pen)
painter.drawLine(self.startLabel.pos(), self.ctrlLabel1.pos())
painter.drawLine(self.ctrlLabel2.pos(), self.endLabel.pos())
path = QPainterPath()
path.moveTo(self.startLabel.pos())
path.cubicTo(self.ctrlLabel1.pos(), self.ctrlLabel2.pos(), self.endLabel.pos())
painter.setPen(Qt.red)
painter.drawPath(path)
painter.end()
def mousePressEvent(self, event):
w = self.childAt(event.pos())
if w is None:
return
if w.inherits('QLabel'):
self.selectedWidget = w
# if event.button() == Qt.LeftButton:
# print("Left mouse button pressed")
def mouseMoveEvent(self, event):
if self.selectedWidget:
self.selectedWidget.move(event.pos())
self.update()
def mouseReleaseEvent(self, event):
self.selectedWidget = None
if __name__ == '__main__':
app = QApplication([])
window = MainWindow()
window.show()
app.exec_()
效果图:
pyqt中通过鼠标事件绘制贝塞尔曲线
代码:
from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsScene, QGraphicsView, QWidget, QLabel
from PyQt5.QtGui import QPainterPath, QPainter, QPen
from PyQt5.QtCore import Qt, QPoint
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.resize(600, 600)
self.label_size = 10
self.style_point = 'QLabel{background-color:red}'
self.style_ctrl_point = 'QLabel{background-color:green}'
self.selectedWidget = None
self.point_array = []
self.point_ctrl_array = []
self.left_button_down = False
def addPoint(self, pos, pos_type):
print('pos:', pos, pos_type)
pos_label = QLabel(self)
pos_label.resize(self.label_size, self.label_size)
if pos_type == 0:
pos_label.setStyleSheet(self.style_point)
elif pos_type == 1:
pos_label.setStyleSheet(self.style_ctrl_point)
pos_label.move(pos)
pos_label.show()
self.update()
return pos_label
def paintEvent(self, event):
if len(self.point_array) < 1:
self.update()
return
if len(self.point_array) != len(self.point_ctrl_array)/2:
return
pen = QPen(Qt.black, 2)
painter = QPainter()
painter.begin(self)
painter.setPen(pen)
for i in range(len(self.point_array)):
painter.drawLine(self.point_array[i].pos(), self.point_ctrl_array[i*2].pos())
painter.drawLine(self.point_array[i].pos(), self.point_ctrl_array[i*2+1].pos())
if len(self.point_array) == 1:
painter.end()
return
path = QPainterPath()
path.moveTo(self.point_array[0].pos())
for i in range(1, len(self.point_array)):
path.cubicTo(self.point_ctrl_array[(i-1)*2+1].pos(), self.point_ctrl_array[i*2].pos(),
self.point_array[i].pos())
pen_curve = QPen(Qt.red, 2)
painter.setPen(pen_curve)
painter.drawPath(path)
painter.end()
self.update()
def mousePressEvent(self, event):
w = self.childAt(event.pos())
print('widget:', w)
if w is None:
self.left_button_down = True
point = self.addPoint(event.pos(), 0)
self.point_array.append(point)
self.update()
return
if w.inherits('QLabel'):
self.selectedWidget = w
# if event.button() == Qt.LeftButton:
# print("Left mouse button pressed")
def mouseMoveEvent(self, event):
if self.selectedWidget:
self.selectedWidget.move(event.pos())
else:
if self.left_button_down:
right_pos = event.pos()
tmp_pos = self.point_array[-1].pos()
delta_value = [right_pos.x()-tmp_pos.x(), right_pos.y()-tmp_pos.y()]
left_pos = QPoint(tmp_pos.x()-delta_value[0], tmp_pos.y()-delta_value[1])
if len(self.point_array) == len(self.point_ctrl_array) / 2:
self.point_ctrl_array[-2].move(left_pos)
self.point_ctrl_array[-1].move(right_pos)
else:
self.point_ctrl_array.append(self.addPoint(left_pos, 1))
self.point_ctrl_array.append(self.addPoint(right_pos, 1))
self.update()
def mouseReleaseEvent(self, event):
self.selectedWidget = None
self.left_button_down = False
if len(self.point_array) == len(self.point_ctrl_array)/2:
return
else:
pos_step = 20
last_pos = self.point_array[-1].pos()
left_pos = QPoint(last_pos.x()-pos_step, last_pos.y()+pos_step)
right_pos = QPoint(last_pos.x()+pos_step, last_pos.y()-pos_step)
self.point_ctrl_array.append(self.addPoint(left_pos, 1))
self.point_ctrl_array.append(self.addPoint(right_pos, 1))
self.update()
if __name__ == '__main__':
app = QApplication([])
window = MainWindow()
window.show()
app.exec_()
效果图:
纯numpy实现贝塞尔曲线计算
地址:https://github.com/torresjrjr/Bezier.py
效果图:
源码:
"""Bezier, a module for creating Bezier curves.
Version 1.1, from < BezierCurveFunction-v1.ipynb > on 2019-05-02
"""
import numpy as np
__all__ = ["Bezier"]
class Bezier():
def TwoPoints(t, P1, P2):
"""
Returns a point between P1 and P2, parametised by t.
INPUTS:
t float/int; a parameterisation.
P1 numpy array; a point.
P2 numpy array; a point.
OUTPUTS:
Q1 numpy array; a point.
"""
if not isinstance(P1, np.ndarray) or not isinstance(P2, np.ndarray):
raise TypeError('Points must be an instance of the numpy.ndarray!')
if not isinstance(t, (int, float)):
raise TypeError('Parameter t must be an int or float!')
Q1 = (1 - t) * P1 + t * P2
return Q1
def Points(t, points):
"""
Returns a list of points interpolated by the Bezier process
INPUTS:
t float/int; a parameterisation.
points list of numpy arrays; points.
OUTPUTS:
newpoints list of numpy arrays; points.
"""
newpoints = []
#print("points =", points, "\n")
for i1 in range(0, len(points) - 1):
#print("i1 =", i1)
#print("points[i1] =", points[i1])
newpoints += [Bezier.TwoPoints(t, points[i1], points[i1 + 1])]
#print("newpoints =", newpoints, "\n")
return newpoints
def Point(t, points):
"""
Returns a point interpolated by the Bezier process
INPUTS:
t float/int; a parameterisation.
points list of numpy arrays; points.
OUTPUTS:
newpoint numpy array; a point.
"""
newpoints = points
#print("newpoints = ", newpoints)
while len(newpoints) > 1:
newpoints = Bezier.Points(t, newpoints)
#print("newpoints in loop = ", newpoints)
#print("newpoints = ", newpoints)
#print("newpoints[0] = ", newpoints[0])
return newpoints[0]
def Curve(t_values, points):
"""
Returns a point interpolated by the Bezier process
INPUTS:
t_values list of floats/ints; a parameterisation.
points list of numpy arrays; points.
OUTPUTS:
curve list of numpy arrays; points.
"""
if not hasattr(t_values, '__iter__'):
raise TypeError("`t_values` Must be an iterable of integers or floats, of length greater than 0 .")
if len(t_values) < 1:
raise TypeError("`t_values` Must be an iterable of integers or floats, of length greater than 0 .")
if not isinstance(t_values[0], (int, float)):
raise TypeError("`t_values` Must be an iterable of integers or floats, of length greater than 0 .")
curve = np.array([[0.0] * len(points[0])])
for t in t_values:
#print("curve \n", curve)
#print("Bezier.Point(t, points) \n", Bezier.Point(t, points))
curve = np.append(curve, [Bezier.Point(t, points)], axis=0)
#print("curve after \n", curve, "\n--- --- --- --- --- --- ")
curve = np.delete(curve, 0, 0)
#print("curve final \n", curve, "\n--- --- --- --- --- --- ")
return curve
测试代码:
from Bezier import Bezier
import numpy as np
import matplotlib.pyplot as plt
t_points = np.arange(0, 1.1, 0.05) #................................. Creates an iterable list from 0 to 1.
points1 = np.array([[0, 0], [0, 8], [5, 10], [9, 7]]) #.... Creates an array of coordinates., [4, 3]
curve1 = Bezier.Curve(t_points, points1) #......................... Returns an array of coordinates.
print(len(curve1))
plt.figure()
plt.plot(
curve1[:, 0], # x-coordinates.
curve1[:, 1] # y-coordinates.
)
plt.plot(
points1[:, 0], # x-coordinates.
points1[:, 1], # y-coordinates.
'ro:' # Styling (red, circles, dotted).
)
plt.grid()
plt.show()
help(Bezier)