在原点缩放 QPolygon

Scaling QPolygon on its origin

我正在尝试缩放位于 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,结果似乎更接近我需要的结果。但是,在这种情况下,原点显然是错误的。这个答案似乎更接近我的需要,这只是偶然的运气吗?

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

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

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

formula can be found in the Wikipedia article linked above, while a valid python implementation is available in this 答案。

我修改了那个答案的公式以接受一系列 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 处的 origin/reference 创建基本形状,然后 移动 x, y 处的项目。

对于像您这样的复杂多边形,可能是将多边形的质心平移到 0, 0,然后将其移动到实际的质心坐标:

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

可能 使开发更容易(映射点将始终与项目位置一致),而且您不需要更改变换原点point another 让反向映射更简单。