You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

115 lines
4.0 KiB
Python

# This Python file uses the following encoding: utf-8
import sys
from PySide6 import QtCore, QtWidgets
from PySide6.QtCore import QLineF, QPoint, QPointF, QRectF
from PySide6.QtGui import QColor, QPainter, QPainterPath, QPen
def bezier(points, t):
cvs = points[:]
while len(cvs) >= 2:
newCvs = []
for i in range(len(cvs) - 1):
newCvs.append(cvs[i] * (1.0 - t) + cvs[i+1] * t)
cvs = newCvs
p = cvs[0]
return p
class MyWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('Spline')
self.points = [QPointF(100, 100), QPointF(700, 500)]
self.pointSize = 10
self.activePoint = -1
self.mousePress = QPoint()
self.mouseDrag = True
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
# paint (see PathStrokeRenderer::paint())
painter.setPen(QtCore.Qt.NoPen)
path = QPainterPath()
for i in range(len(self.points)):
if i == 0:
path.moveTo(self.points[0])
else:
path.lineTo(self.points[i])
# draw path
pen = QPen()
painter.strokePath(path, pen)
if len(self.points) >= 3:
spline = QPainterPath()
pen = QPen()
d = 0.0
for i in range(len(self.points) - 1):
d += QLineF(self.points[i], self.points[i+1]).length()
d = int(d / float(len(self.points) - 1))
for i in range(d + 1):
t = float(i) / float(d)
p = bezier(self.points, t)
if i == 0:
spline.moveTo(p)
else:
spline.lineTo(p)
painter.strokePath(spline, pen)
# draw CVs
painter.setPen(QColor(50, 100, 120, 200))
painter.setBrush(QColor(200, 200, 210, 120))
for point in self.points:
painter.drawEllipse(QRectF(point.x() - self.pointSize,
point.y() - self.pointSize,
self.pointSize * 2,
self.pointSize * 2))
painter.end()
def mousePressEvent(self, event):
self.activePoint = -1
distance = -1
for i in range(len(self.points)):
point = self.points[i]
d = QLineF(event.position().toPoint(), point).length()
if distance < 0 and d < 8 * self.pointSize or d < distance:
distance = d
self.activePoint = i
if self.activePoint != -1:
self.mouseMoveEvent(event)
def mouseMoveEvent(self, event):
# if we've moved more then 25 pixels, assume user is dragging
if (not self.mouseDrag and
QPoint(self.mousePress - event.position().toPoint()).manhattenLength() > 25):
self.mouseDrag = True
if self.mouseDrag and self.activePoint >= 0 and self.activePoint < len(self.points):
self.points[self.activePoint] = QPointF(event.position().toPoint())
self.update()
def keyPressEvent(self, event):
key = event.key()
# Qt::Key_Plus 0x2b
if key == QtCore.Qt.Key_Plus:
# add new point
if len(self.points) == 2:
point1 = self.points[0]
point2 = self.points[1]
newPoint = (point1 + point2) / 2.0
self.points.insert(1, newPoint)
else:
newPoints = []
newPoints.append(self.points[0])
degree = len(self.points) - 1
for i in range(degree):
t = 1.0 - float(i + 1) / float(degree + 1)
newPoints.append(self.points[i] * (1.0 - t) + self.points[i+1] * t)
newPoints.append(self.points[-1])
self.points = newPoints
self.update()
if __name__ == "__main__":
app = QtWidgets.QApplication([])
widget = MyWidget()
widget.resize(800, 600)
widget.show()
sys.exit(app.exec_())