在原点缩放 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 让反向映射更简单。
我正在尝试缩放位于 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 让反向映射更简单。