如何正确平滑 QPainterPath?

问题描述 投票:0回答:1

我正在开发路径绘图工具,并发表了一篇关于如何实现更流畅绘图的文章。我对

cubicTo()
quadTo()
做了一些修补,但这些确实没有多大帮助。我不知道我的数学是否正确,或者我如何计算出更平滑的绘制路径。我的上一篇文章中有人推荐了一系列数学公式(多项式)来实现平滑的路径。任何代码片段表示赞赏。

    def on_path_draw_start(self, event):
        # Check the button being pressed
        if event.button() == Qt.LeftButton:
            # Create a new path
            self.path = QPainterPath()
            self.path.moveTo(self.mapToScene(event.pos()))
            self.last_point = self.mapToScene(event.pos())

            # Set drag mode
            self.setDragMode(QGraphicsView.NoDrag)

    def on_path_draw(self, event):
        # Check the buttons
        if event.buttons() == Qt.LeftButton:
            # Calculate average point for smoother curve
            mid_point = (self.last_point + self.mapToScene(event.pos())) / 2.0
            
            # Use the mid_point as control point for quadTo
            self.path.quadTo(self.last_point, mid_point)
            self.last_point = self.mapToScene(event.pos())
            
            # Remove temporary path if it exists
            if self.temp_path_item:
                self.canvas.removeItem(self.temp_path_item)

            # Load temporary path as QGraphicsItem to view it while drawing
            self.temp_path_item = CustomPathItem(self.path)
            self.temp_path_item.setPen(self.pen)
            if self.button3.isChecked():
                self.temp_path_item.setBrush(QBrush(QColor(self.stroke_fill_color)))
            self.temp_path_item.setZValue(2)
            self.canvas.addItem(self.temp_path_item)

            # Create a custom tooltip for the current coords
            scene_pos = self.mapToScene(event.pos())
            QToolTip.showText(event.pos(), f'dx: {round(scene_pos.x(), 1)}, dy: {round(scene_pos.y(), 1)}')

            self.canvas.update()

    def on_path_draw_end(self, event):
        # Check the buttons
        if event.button() == Qt.LeftButton:
            # Calculate average point for smoother curve
            mid_point = (self.last_point + self.mapToScene(event.pos())) / 2.0
            
            # Use the mid_point as control point for quadTo
            self.path.quadTo(self.last_point, mid_point)
            self.last_point = self.mapToScene(event.pos())

            # Check if there is a temporary path (if so, remove it now)
            if self.temp_path_item:
                self.canvas.removeItem(self.temp_path_item)

            # If stroke fill button is checked, close the subpath
            if self.button3.isChecked():
                self.path.closeSubpath()

            self.canvas.update()

            # Load main path as QGraphicsItem
            path_item = CustomPathItem(self.path)
            path_item.setPen(self.pen)
            path_item.setZValue(0)

            # If stroke fill button is checked, set the brush
            if self.button3.isChecked():
                path_item.setBrush(QBrush(QColor(self.stroke_fill_color)))

            # Add item
            self.canvas.addItem(path_item)

            # Set Flags
            path_item.setFlag(QGraphicsItem.ItemIsSelectable)
            path_item.setFlag(QGraphicsItem.ItemIsMovable)

            # Set Tooltop
            path_item.setToolTip('MPRUN Path Element')

我知道这是一项艰巨的任务,但我需要一些指导。无论如何,我都不是数学奇才。或者也许我什至不需要数学,我真的不知道。

python-3.x qt pyqt pyqt5 qgraphicsscene
1个回答
0
投票

对于那些好奇的人,我解决了我的困境(用数学)

我基本上只是用 Numpy 和 SciPy 实现了平滑算法。这是代码:

    def smooth_path(self, path):
        vertices = [(point.x(), point.y()) for point in path.toSubpathPolygons()[0]]
        x, y = zip(*vertices)
        tck, u = splprep([x, y], s=0)
        smooth_x, smooth_y = splev(np.linspace(0, 1, len(vertices) * 2), tck)  # Reduce the granularity

        smoothed_vertices = np.column_stack((smooth_x, smooth_y))
        simplified_vertices = approximate_polygon(smoothed_vertices, tolerance=2.0)  # Adjust the tolerance as needed

        smooth_path = QPainterPath()
        smooth_path.moveTo(simplified_vertices[0][0], simplified_vertices[0][1])

        for i in range(1, len(simplified_vertices) - 2, 3):
            smooth_path.cubicTo(
                simplified_vertices[i][0], simplified_vertices[i][1],
                simplified_vertices[i + 1][0], simplified_vertices[i + 1][1],
                simplified_vertices[i + 2][0], simplified_vertices[i + 2][1]
            )

        return smooth_path

要实现此功能,您只需使用以下函数:

my_graphics_path_item.smooth_path(my_graphics_path_item.path())

然后将项目添加到场景中,依此类推...

您还可以更改平滑度的容差(我发现2.0效果最好)

我希望这可以帮助任何解决这个特殊问题的人。

© www.soinside.com 2019 - 2024. All rights reserved.