在python中检测任意形状的内外角的最佳算法

时间:2013-11-27 20:50:55

标签: python algorithm geometry

我一直在研究python 3.2.5中的二维对象创建程序,它处理任意形状的操作并计算它们之间的碰撞检测。该程序允许您输入形状的坐标,并从那里它将执行您想要它做的任何事情(将形状绘制到屏幕,扩展边框,操纵单个坐标,使其对称等)。

但是在尝试计算任意多边形的内角时遇到了问题。虽然我用来计算角度的算法在技术上输出了正确的角度,但我无法判断程序是否吐出内角或外角(因为用户输入的任意形状可能有凹顶点)

在纸面上这看起来像小菜一碟,因为你可以看到形状,你可以自动解释哪个角度是内部和外部。但由于程序只存储坐标的值,并且实际上并没有在视觉上创建对象来推断数据,因此这个问题变得有点难以解决。

所以我的问题是:

我应该使用什么方法来计算两条线之间的角度,以及如何使用它来确定内部和外部角度之间的差异?

例如,如果我的形状有坐标((30,50),(35,47),(40,50),(37,43),(35,35),(33,43) )(最终看起来有点像倒凹尖的尖底),我可以很容易地计算出线条之间的角度,但我计算的角度是个谜。

4 个答案:

答案 0 :(得分:4)

正如Jesse建议的那样,首先需要按顺序保留顶点列表。我会建议逆时针。使用点积来查找叉积的角度和符号,以告诉您它的哪一侧。对于以逆时针顺序存储它们,正数将是内角

# Its a square with the top edge poked in
points = [
    ( 1.0,  1.0),
    ( 0.0,  0.0),
    (-1.0,  1.0),
    (-1.0, -1.0),
    ( 1.0, -1.0)]


def angle(x1, y1, x2, y2):
    # Use dotproduct to find angle between vectors
    # This always returns an angle between 0, pi
    numer = (x1 * x2 + y1 * y2)
    denom = sqrt((x1 ** 2 + y1 ** 2) * (x2 ** 2 + y2 ** 2))
    return acos(numer / denom) 


def cross_sign(x1, y1, x2, y2):
    # True if cross is positive
    # False if negative or zero
    return x1 * y2 > x2 * y1

for i in range(len(points)):
    p1 = points[i]
    ref = points[i - 1]
    p2 = points[i - 2]
    x1, y1 = p1[0] - ref[0], p1[1] - ref[1]
    x2, y2 = p2[0] - ref[0], p2[1] - ref[1]

    print('Points', p1, ref, p2)
    print('Angle', angle(x1, y1, x2, y2))
    if cross_sign(x1, y1, x2, y2):
        print('Inner Angle')
    else:
        print('Outer Angle')
    print('')

答案 1 :(得分:3)

找到两个向量之间的符号角的黄金标准是atan2(cross(a,b)), dot(a,b))。各个角度的高精度和坚固性。 (在2D中,cross是垂直点积ax*by-ay*bx。在三维中,使用叉积的长度;其方向是旋转轴。)

有些事要做不到:

  • 涉及acos的任何事情。 Arccosine是一种代码气味。它受限于范围,不会给你标志,需要手动论证夹紧,并且其极值精度较差。如果您发现自己使用它,请尝试别的。
  • 涉及线斜坡的任何事情。准确性差,当然对于垂直向量是不确定的。
  • 根据额外测试手动选择角度范围。这可能会导致轴附近的不连续。

答案 2 :(得分:0)

我不知道你尝试过哪种算法,但通常这个问题是通过以相同的顺序(例如,顺时针方向)存储点列表来解决的,这样每次都可以在三个点上运行角度计算按顺序,你总是得到形状的同一面(比如内角)。

答案 3 :(得分:0)

有一种简单的算法可以检查多边形内是否存在一个点。找到here,他也很好地描述了它是如何工作的。以下是用C语言编写的,但很容易转换为python。

int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy)
 {
  int i, j, c = 0;
   for (i = 0, j = nvert-1; i < nvert; j = i++) {
    if ( ((verty[i]>testy) != (verty[j]>testy)) &&
 (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
       c = !c;
  }
  return c;
}

无论如何,您可以使用此算法通过在没有测试顶点的情况下对多边形进行检查来检查顶点是凹还是凸。

所以如果你有一个带有顶点a,b,c,d的四边形,你想看看b是凸面还是凹面,你可以测试b是否在三角形a,c,d内。如果它在里面是凸的,如果不是凹的,如果它们都不是顶点。

根据天气的知识,它是凸的或凹的,你应该能够解析出哪个是内角,哪个是外部。