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
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_())
|