【问题标题】:Convex hull in IronpythonIronpython 中的凸壳
【发布时间】:2020-08-12 22:45:29
【问题描述】:

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

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 链接到图片说明。

Convex Hull Algorithm

期望的输出:

【问题讨论】:

  • 那么...问题是什么?
  • stack = [p0, points.pop(0), points.pop(1)] 似乎不对;您正在跳过一个点,即在此语句之前位于 points[1] 的点(pop(0) 将该点移动到索引 0,pop(1) 然后在索引 2 处获取该点)。
  • 请提供预期的minimal, reproducible example。显示中间结果与您的预期不同的地方。支持图很有帮助,但请提供一个简单失败示例,跟踪您的错误点,并将输出作为点列表提供给我们。
  • 请添加一些说明问题的简单测试数据。
  • 澄清:包括一些证明意外行为的有效数据。数据应采用某种格式,以便某人(仅使用您的帖子内容)能够将其用作程序的输入,并产生相同的输出。此外,您的程序应该无需任何进一步修改即可运行(现在它无法运行)。

标签: python ironpython convex-hull


【解决方案1】:

我在 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()

【讨论】:

    猜你喜欢
    • 2016-09-28
    • 2012-06-28
    • 2015-02-07
    • 2018-05-17
    • 2012-06-13
    • 2021-11-07
    • 1970-01-01
    • 2012-10-10
    • 2013-07-09
    相关资源
    最近更新 更多