Ironpython 中的凸包

Convex hull in Ironpython

我正在尝试从点列表中获取凸包,这是基于 Graham 扫描方法。

points = []
for element in elements:
    #getpoints function returns four corners of element bounding box with z overwritten to 0 so that we are working in 2D coordinates.
    for p in getPoints(element):
        points.append(p)
p0 = None
points.sort(key=lambda x: x.X)
points.sort(key=lambda x: x.Y)
#isolate lower left point
p0 = points.pop(0)
#sort by angle to x axis
points.sort(key=lambda x: DB.XYZ.BasisX.AngleTo(x-p0))
# We know the second point is correct, we are starting by checking the third point.
stack = [p0, points.pop(0), points.pop(1)]
while len(points) > 0:
    vector1 = stack[-2] - stack[-1]
    vector2 = points[0] - stack[-1]
    angle1 = math.atan2(vector1.X, vector1.Y)
    angle2 = math.atan2(vector2.X, vector2.Y)
    angleDiff = angle1 - angle2
    if angleDiff >= 0:
        stack.pop(-1)
        ######stack.append(points.pop(0))  I don't think this is needed, but it doesn't solve my problem when removed.
    else:
        stack.append(points.pop(0))
curves = []
for i, point in enumerate(stack):
    curves.append(DB.Line.CreateBound(point, stack[i-1]))

“凸包”的输出显然不正确:

编辑澄清:

获得最左边的最低点。 按照与 x 轴的角度对所有其他点进行排序。 将前三个点添加到堆栈中。 环形, 如果下一个排序点将创建顺时针旋转,则移除堆栈的顶部。 否则,如果它创建逆时针旋转,则将候选排序点添加到堆栈顶部。

我会努力整理一个可重现的案例。

YouTube link 以图形解释。

Convex Hull Algorithm

期望的输出:

我在 python 中重建了它并让它工作。我认为问题在于我计算 angleDiff 的方式,这样更容易查看叉积 z 值的符号。

stack = [p0, points.pop(0), points.pop(1)] 确实会跳过一个值,谢谢 jason。

我在初始示例中也有 vector1 向后。应该是stack[-1] - stack[-2]不是stack[-2] - stack[-1]

import math
import matplotlib.pyplot as plt
from random import randint


class XYZ:
    @property 
    def X(self):
        return self._X
    @X.setter
    def X(self, X):
        self._X = X
    @property 
    def Y(self):
        return self._Y
    @Y.setter
    def Y(self, Y):
        self._X = Y

    def __init__(self,x,y):
        self._X = x
        self._Y = y

    def __add__(self, other):
        x = self.X + other.X
        y = self.Y + other.Y
        return XYZ(x, y)
    
    def __sub__(self, other):
        x = self.X - other.X
        y = self.Y - other.Y
        return XYZ(x, y)
    
    def ccw(self, other):
        return (self.X*other.Y - self.Y*other.X) > 0

### Rand Points
points = []
for i in range(100):
    points.append(XYZ(randint(0,100), randint(0,100)))
### Static Points
# points.append(XYZ(0,0))
# points.append(XYZ(0,1))
# points.append(XYZ(15,0))
# points.append(XYZ(0,6))
# points.append(XYZ(2,2))
# points.append(XYZ(0,8))
# points.append(XYZ(12,16))
# points.append(XYZ(12,2))
# points.append(XYZ(8,1))

for i, point in enumerate(points):
    plt.scatter([point.X], [point.Y])

points.sort(key=lambda x: x.X)
points.sort(key=lambda x: x.Y)
p0 = points.pop(0)
points.sort(key=lambda x: math.atan2((x-p0).Y, (x-p0).X))
stack = [p0]
stack.append(points.pop(0))
stack.append(points.pop(0))
while len(points) > 0:
    vector1 = stack[-1] - stack[-2]
    vector2 = points[0] - stack[-1]
    if not vector1.ccw(vector2):
        stack.pop(-1)
    else:
        stack.append(points.pop(0))

for i, point in enumerate(stack):
    plt.plot([point.X, stack[i-1].X], [point.Y, stack[i-1].Y])

plt.show()