如下所示,我有一个包含整数点的多边形:
type
TPoint = array [1 .. 2] of Integer;
TPointArray = array of TPoint;
边缘上的点按时钟顺序给出,我想删除中间点(标记为红色)。
例如,给定任意连续3点A,B,C如果B恰好在A和C之间的线上,则可以安全地移除B.
如何快速有效地完成这项工作?
我的Psedu代码是:
procedure RemovePoints(var pt: TPointArray);
var
i, j, sz, k, u: integer;
xoffset, yoffset: integer;
begin
sz := High(pt);
i := 0;
while (True) do
begin
Inc(i);
// points represent array index, so can't be negative,
// we use -1 to mark deletion
if (pt[i][1] = -1) then continue;
if (i = 0) then
begin
break; // finish a loop
end;
j := i + 1;
if (j > sz) then
begin
j := 0;
end;
k := j + 1;
if (k > sz) then
begin
k := 0;
end;
if ((pt[i][1] - pt[j][1] = pt[j][1] - pt[k][1]) and ((pt[i][2] - pt[j][2] = pt[j][2] - pt[k][2]))) then
begin
// remove pt[j];
pt[j][1] := -1;
end;
end;
// TODO: shrink the array to remove deleted points
end;
答案 0 :(得分:3)
浏览列表并考虑相邻点的三元组 p , q 和 r 。确定前向向量 pq 和 qr 并检查它们是否共线。如果是这样,如果它们面向同一方向,请标记删除点。
在第二遍中,过滤掉标记的点。
如果两个向量的叉积是空向量,则它们是共线的。平面中矢量的叉积仅具有垂直于该平面的一个分量。所以检查:
\b
如果你想确保不删除狭长柄的尖点,请检查矢量的点积是否为正:
pq.x * qr.y - pq.y * qr.x == 0
因为你有整数坐标,所以这些检查应该很简单。
这是一个使用您的命名法和类型的解决方案。它在单独的数组中跟踪要删除的节点。 (您无法使用虚拟值标记坐标,因为在确定是否删除下一个节点时仍需要坐标完整。)
pq.x * qr.x + pq.y * qr.y > 0
答案 1 :(得分:0)
我们已经知道我们可以使用斜率截距方程完美地定义一条线。这意味着我们还可以使用斜率和该线所经过的一个点唯一地定义一条线。
从多边形点列表的第一个点开始,计算通过第一个点和下一个点的线的斜率。这是您的第一个斜率值。将此值存储为currentSlope
。创建newPointList
以存储新的边缘点。下一个:
currentSlope
相同
continue
循环回到第1步。currentSlope
不同
newPointList
中,并将currentSlope
的值更新为第二个和第三个点之间的斜率。当迭代结束时,newPointList
将只包含多边形的顶点。