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。确定前向向量pqqr,并检查它们是否共线。如果是,并且它们朝向同一方向,则标记要删除的点

在第二遍中,过滤掉标记点

如果两个向量的叉积是零向量,则它们是共线的。平面中向量的叉积只有一个垂直于该平面的分量。因此,请检查:

pq.x * qr.y - pq.y * qr.x == 0
如果要确保不删除平移手柄的尖锐点,请检查向量的点积是否为正:

pq.x * qr.x + pq.y * qr.y > 0
因为您有整数坐标,所以这些检查应该很简单

这里有一个使用您的命名法和类型的解决方案。它在一个单独的数组中跟踪要删除的节点。(无法使用虚拟值标记坐标,因为在确定是否删除下一个节点时,仍需要坐标保持不变。)


我们已经知道,我们可以用斜率截距方程完美地定义一条直线。这意味着,我们还可以使用斜率和该线通过的一个点来唯一地定义一条线

从多边形点列表中的第一个点开始,计算通过第一个点和下一个点的直线的坡度。这是您的第一个坡度值。将此值存储为
currentSlope
。创建用于存储新边点的
newPointList
。下一步:

  • 从第三个点开始遍历所有点
  • 如果第二点和第三点的斜率与当前斜率相同
  • 这意味着第二个点与第一个和第三个点在同一条线上,所以跳过这一点
  • 继续
    循环返回到步骤1
  • 如果第二点和第三点的斜率不同于
    currentSlope
  • 这意味着由第一点和第二点定义的边与由第二点和第三点定义的边不同
  • 将第一个点和第二个点存储在
    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;