1

PyQt4 中是否有任何功能可以帮助我确定一个点是否在 QPolygon 的周长上?例如:

from PyQt4 import QtGui
from PyQt4.QtCore import Qt, QPoint as QP

polygon = QtGui.QPolygon([QP(0, 1),  QP(3,7), QP(4, 6), QP(4,3), QP(2,1), QP(0,1])

如果我将 QP(1,3)、QP(4,5)、QP(3,2) 或 QP(1,1) 传递给它,该函数应该返回 true。

4

1 回答 1

1

这确实是一项艰巨的任务。我玩了很多方法QPolygonlike intersected, unitedsubtracted但都没有带来任何成功。

例如,我认为这可能有效:复制多边形,将点添加到副本中,然后检查结果是否为空。如果该点位于多边形的周长上,则原件和副本都应具有相同的形状,因此结果应为空。

def on_perimeter(poly, point):
    poly2 = poly + QtGui.QPolygon()  # copy the polygon
    poly2.add(point)
    return poly.subtracted(poly2).isEmpty()

然而,一个多边形似乎是按照给定点的顺序“绘制”的,因此如果你只是添加点,这会导致一些其他形状。例如,考虑(0,0) (0,2) (2.2) (2,0)形成正方形的点并且您要检查(0,1)。然后,如果您只是在末尾添加点,这将(2,0)(0,1)(0,1)与“连接”,(0,0)因为多边形必须是封闭的形式。这会给出其他形状。因此,您必须在正确的位置插入点才能获得相同的形状。对于这个例子,它会在 之后(0,0)。所以我想,好吧,让我们尝试上面所有可能的排列,并且只有一种配置(及其由旋转和反转产生的转换),这样减法的结果是空的。

import itertools

def on_perimeter(poly, point):
    points = [point]  # the points of the new polygon
    for ii in range(0, poly.size()):
        points += [poly.point(ii)]

    permuts = list(itertools.permutations(points))  # all possible permutations

    checks = 0
    for permut in permuts:
        checks += int(poly.subtracted(QtGui.QPolygon(list(permut))).isEmpty())

    return checks

但不知何故,这也不起作用。尝试您的示例,我得到 forQP(4,5)和for和 forQP(3,2)的值。我所期望的是获得所有分数(因为它们都位于外围)。因为是由点组成的,所以您可以旋转点数次并在颠倒顺序后执行相同操作,因此在导致相同形状中包含不同的配置。此外,如果以相反的方式执行减法(即),我得到的每个点也适用于甚至不在多边形内但不在多边形的点。checks = 10QP(1,1) checks = 20QP(1,3) checks = 0checks = 1212poly26612permutsQtGui.QPolygon(list(permut)).subtracted(poly).isEmpty()True

我在上面的函数中使用unitedandintersected而不是尝试了类似的事情:isEmpty

tmp = QtGui.QPolygon(list(permut))
checks += int(poly.intersected(tmp) == poly.united(tmp))

同样在这里,它应该只评估True该点实际上是否位于周边上。但这False几乎可以回馈我检查您上述示例的每一点。

我没有看QPolygon(如果有的话)方法的源代码,但似乎发生了一些奇怪的事情。

所以我建议你编写一个自己的方法来评估多边形中的所有线,如果点位于其中一条线上。

def on_perimeter(poly, point):
    lines = []
    for ii in range(1, poly.size()):
        p1 = poly.point(ii-1)
        p2 = poly.point(ii)
        lines += [ ( (p1.x(), p1.y()), (p2.x(), p2.y()) ) ]
    lines += [ ( (poly.last.x(), poly.last.y()), (poly.first.x(), poly.first.y()) ) ]

    for line in lines:
        dx = line[1][0] - line[0][0]
        dy = line[1][1] - line[0][1]

        if abs(dx) > abs(dy) and dx*dy != 0 or dx == 0 and dy == 0:  # abs(slope) < 1 and != 0 thus no point with integer coordinates can lie on this line
            continue

        if dx == 0:
            if point.x() == line[0][0] and (point.y()-line[[0][1])*abs(dy)/dy > 0 and (line[1][1]-point.y())*abs(dy)/dy > 0:
                return True

        if dy == 0:
            if point.y() == line[0][1] and (point.x()-line[[0][0])*abs(dx)/dx > 0 and (line[1][0]-point.x())*abs(dx)/dx > 0:
                return True

        dx2 = point.x() - line[0][0]
        dy2 = point.y() - line[0][1]

        if dx*dx2 < 0 or dy*dy2 < 0:
            continue

        if abs(dx) % abs(dx2) == 0 and abs(dy) % abs(dy2) == 0:
            return True

    return False

这似乎有点重,但重要的是只使用整数执行所有计算,因为浮点精度可能会得到错误的结果(QPolygon无论如何只需要整数点)。虽然尚未测试,但它应该可以工作。

于 2014-09-04T14:53:24.050 回答