Algorithm 删除多边形边上重复点的最快方法?
如下图所示,我有一个包含整数点的多边形:Algorithm 删除多边形边上重复点的最快方法?,algorithm,Algorithm,如下图所示,我有一个包含整数点的多边形: 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
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;
< P>遍历列表,考虑相邻点的三个点<强> P,<强> q和<强> R。确定前向向量pq和qr,并检查它们是否共线。如果是,并且它们朝向同一方向,则标记要删除的点 在第二遍中,过滤掉标记点 如果两个向量的叉积是零向量,则它们是共线的。平面中向量的叉积只有一个垂直于该平面的分量。因此,请检查:
pq.x * qr.y - pq.y * qr.x == 0
如果要确保不删除平移手柄的尖锐点,请检查向量的点积是否为正:
pq.x * qr.x + pq.y * qr.y > 0
因为您有整数坐标,所以这些检查应该很简单
这里有一个使用您的命名法和类型的解决方案。它在一个单独的数组中跟踪要删除的节点。(无法使用虚拟值标记坐标,因为在确定是否删除下一个节点时,仍需要坐标保持不变。)
我们已经知道,我们可以用斜率截距方程完美地定义一条直线。这意味着,我们还可以使用斜率和该线通过的一个点来唯一地定义一条线 从多边形点列表中的第一个点开始,计算通过第一个点和下一个点的直线的坡度。这是您的第一个坡度值。将此值存储为
currentSlope
。创建用于存储新边点的newPointList
。下一步:
继续
循环返回到步骤1currentSlope
newPointList
中,并将currentSlope
的值更新为第二个点和第三个点之间的斜率当迭代结束时,
newPointList
将只包含多边形的顶点。到目前为止您是否进行过编码工作?老实说,我的代码非常混乱。我的想法是循环所有点(如果没有标记为重复)作为起点,检查接下来的两个点是否重复……但我觉得代码很恶心。我建议你至少发布一部分,这样人们可以看到你的问题,从而建议你如何改进/提高性能。我会从两个相邻点开始,检查下一个点是否在同一条线上。如果是,则删除第二点;如果没有进步一点。重新编辑问题并粘贴我的代码。不幸的是,这种方法并不适用于所有情况。考虑一个具有4个顶点(100,0)(0,0)(100100)(100,0)的多边形。仅使用叉积度量将删除顶点0和3。添加dot产品测试不会删除任何内容。您真正想要的是删除顶点0或3,但不能同时删除两者。@keith969:Hm,这是一种退化情况,其中两个相邻点相同,因此差分向量是零向量。这个答案只涉及删除中间点。如果要检测重复节点,必须添加另一个检查。是的,技巧似乎是一次删除一个点。使用循环继续尝试删除点,如果未删除任何点,则中断。
procedure RemovePoints(var pt: TPointArray);
Var i, j: Integer;
p, q, r: TPoint;
ax, ay: Integer;
bx, by: Integer;
del: Array of Boolean;
begin
if Length(pt) > 2 then begin
SetLength(del, Length(pt));
for i := 0 to Length(pt) - 1 do del[i] := False;
j := Length(pt) - 1;
p := pt[j - 1];
q := pt[j];
for i := 0 to Length(pt) - 1 do begin
r := pt[i];
ax := q[1] - p[1];
ay := q[2] - p[2];
bx := r[1] - q[1];
by := r[2] - q[2];
if (ax*by - ay*bx = 0) and (ax*bx + ay*by > 0) then del[j] := True;
p := q;
q := r;
j := i;
end;
j := 0;
for i := 0 to Length(pt) - 1 do begin
if not del[i] then begin
pt[j] := pt[i];
inc(j);
end;
end;
SetLength(pt, j);
end;
end;