3

我正在尝试缩放位于其原点的 QGraphicsScene 的 QGraphicsView 上的 QPolygonF。

然而,即使在将多边形 (poly_2) 平移到其原点之后(使用 QPolygon.translate() 和通过 boundingRect (x+width)/2 和 (y+height)/2 接收到的多边形的中心坐标),新的多边形仍然放置在错误的位置。

蓝色多边形应该根据poly_2的原点进行缩放(请看下图,黑色是原始多边形,蓝色多边形是下面代码的结果,橙色多边形代表预期的结果) 在此处输入图像描述

我认为问题可能是坐标来自全球并且应该是本地的,但不幸的是这确实解决了这个问题。

这是代码:

import PyQt5
from PyQt5 import QtCore

import sys
import PyQt5
from PyQt5.QtCore import *#QPointF, QRectF
from PyQt5.QtGui import *#QPainterPath, QPolygonF, QBrush,QPen,QFont,QColor, QTransform
from PyQt5.QtWidgets import *#QApplication, QGraphicsScene, QGraphicsView, QGraphicsSimpleTextItem


poly_2_coords= [PyQt5.QtCore.QPointF(532.35, 274.98), PyQt5.QtCore.QPointF(525.67, 281.66), PyQt5.QtCore.QPointF(518.4, 292.58), PyQt5.QtCore.QPointF(507.72, 315.49), PyQt5.QtCore.QPointF(501.22, 326.04), PyQt5.QtCore.QPointF(497.16, 328.47), PyQt5.QtCore.QPointF(495.53, 331.71), PyQt5.QtCore.QPointF(488.24, 339.02), PyQt5.QtCore.QPointF(480.94, 349.56), PyQt5.QtCore.QPointF(476.09, 360.1), PyQt5.QtCore.QPointF(476.89, 378.76), PyQt5.QtCore.QPointF(492.3, 393.35), PyQt5.QtCore.QPointF(501.22, 398.21), PyQt5.QtCore.QPointF(527.17, 398.21), PyQt5.QtCore.QPointF(535.28, 390.1), PyQt5.QtCore.QPointF(540.96, 373.89), PyQt5.QtCore.QPointF(539.64, 356.93), PyQt5.QtCore.QPointF(541.46, 329.0), PyQt5.QtCore.QPointF(543.39, 313.87), PyQt5.QtCore.QPointF(545.83, 300.89), PyQt5.QtCore.QPointF(545.83, 276.56), PyQt5.QtCore.QPointF(543.39, 267.64), PyQt5.QtCore.QPointF(537.81, 268.91)]





def main():
    app = QApplication(sys.argv)



    scene = QGraphicsScene()
    view = QGraphicsView(scene)


    pen = QPen(QColor(0, 20, 255))


    scene.addPolygon(QPolygonF(poly_2_coords))
    poly_2 = QPolygonF(poly_2_coords)
    trans = QTransform().scale(1.5,1.5)
    #poly_22 = trans.mapToPolygon(QRect(int(poly_2.boundingRect().x()),int(poly_2.boundingRect().y()),int(poly_2.boundingRect().width()),int(poly_2.boundingRect().height())))
    #trans.mapToPolygon()
    #scene.addPolygon(QPolygonF(poly_22),QPen(QColor(0, 20, 255)))

    poly_2.translate((poly_2.boundingRect().x()+poly_2.boundingRect().width())/2,(poly_2.boundingRect().y()+poly_2.boundingRect().height())/2)


    print(f'poly_2.boundingRect().x() {poly_2.boundingRect().x()}+poly_2.boundingRect().width(){poly_2.boundingRect().width()}')
    trans = QTransform().scale(1.4,1.4)
    #poly_2.setTransformOriginPoint()
    poly_22 = trans.map(poly_2)

    scene.addPolygon(poly_22,QPen(QColor(0, 20, 255)))


    view.show()

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

编辑:我尝试将多边形保存为 QGraphicsItem,并根据 bbox 的中间 X、Y 设置其转换原点,然后从全局映射到场景,但没有运气:新多边形仍然绘制到错误的位置。

    poly_2 = QPolygonF(poly_2_coords)
    poly = scene.addPolygon(poly_2)
    point = QPoint((poly_2.boundingRect().x()+poly_2.boundingRect().width())/2,(poly_2.boundingRect().y()+poly_2.boundingRect().height())/2)        
    poly.setTransformOriginPoint(point)
    poly.setScale(3)

如果将点替换为仅等于边界矩形的 X,Y,则结果似乎更接近我的需要。但是,在这种情况下,原点显然是错误的。这只是随机的运气,这个答案似乎更接近我的需要吗?

在此处输入图像描述

4

1 回答 1

2

在考虑平移问题之前,有一个更重要的方面需要考虑:如果要基于多边形的中心创建变换,则必须找到该中心。该点称为质心,即任何多边形的几何中心。

虽然所有基本几何形状都有简单的公式,但找到具有任意数量顶点的(可能是不规则的)多边形的质心要复杂一些。

使用顶点的算术平均值不是一个可行的选择,因为即使在一个简单的正方形中,单边也可能有多个点,这会将计算出的“中心”移向这些点。

公式可以在上面链接的 Wikipedia 文章中找到,而答案中提供了有效的 python 实现。

我修改了该答案的公式以接受一系列 QPoints,同时提高可读性和性能,但概念保持不变:

def centroid(points):
    if len(points) < 3:
        raise ValueError('At least 3 points are required')
    # https://en.wikipedia.org/wiki/Centroid#Of_a_polygon
    # https://en.wikipedia.org/wiki/Shoelace_formula
    # computation uses concatenated pairs from the sequence, with the
    # last point paired to the first one:
    # (p[0], p[1]), (p[1], p[2]) [...] (p[n], p[0])
    area = cx = cy = 0
    p1 = points[0]
    for p2 in points[1:] + [p1]:
        shoelace = p1.x() * p2.y() - p2.x() * p1.y()
        area += shoelace
        cx += (p1.x() + p2.x()) * shoelace
        cy += (p1.y() + p2.y()) * shoelace
        p1 = p2
    A = 0.5 * area
    factor = 1 / (6 * A)
    return cx * factor, cy * factor

然后,您有两个选项,具体取决于您要对结果项目执行的操作。

缩放项目

在这种情况下,您创建一个 QGraphicsPolygonItem 就像原来的一样,然后使用上面的公式设置它的变换原点,并对其进行缩放:

    poly_2 = QtGui.QPolygonF(poly_2_coords)
    item2 = scene.addPolygon(poly_2, QtGui.QPen(QtGui.QColor(0, 20, 255)))
    item2.setTransformOriginPoint(*centroid(poly_2_coords))
    item2.setScale(1.5)

使用 QTransform

对于 Qt 转换,必须特别注意,因为缩放始终0, 0用作原点。

要围绕指定点进行缩放,您必须首先将矩阵平移到该点,然后应用缩放,最后将矩阵平移恢复到其原点:

    poly_2 = QtGui.QPolygonF(poly_2_coords)
    cx, cy = centroid(poly_2_coords)
    trans = QtGui.QTransform()
    trans.translate(cx, cy)
    trans.scale(1.5, 1.5)
    trans.translate(-cx, -cy)
    poly_2_scaled = trans.map(poly_2)
    scene.addPolygon(poly_2_scaled, QtGui.QPen(QtGui.QColor(0, 20, 255)))

这正是 QGraphicsItems 在使用基本setScale()setRotation()转换时所做的。


形状原点和项目位置

请记住,QGraphicsItems总是以其位置在创建0, 0
这可能看起来并不明显,尤其是对于基本形状:当你创建一个 QGraphicsRectItem 给它x, y, width, height时,位置仍然是0, 0。在处理复杂的几何管理时,通常最好使用原点/参考创建基本形状,0, 0然后项目移动到x, y

对于像您这样的复杂多边形,一种可能性是在 处平移多边形的质心0, 0,然后在实际质心坐标处移动它:

    item = scene.addPolygon(polygon.translated(-cx, -cy))
    item.setPos(cx, cy)
    item.setScale(1.5)

可能会使开发变得更容易(映射点将始终与项目位置保持一致),并且您不再需要更改变换原点这一事实使反向映射更加简单。

于 2022-01-19T00:16:39.237 回答