确定分段线的交叉点

时间:2013-07-17 15:12:20

标签: c++ math geometry line-intersection

/我需要确定由多个线段定义的一对线是否相交,例如由(0,0), (1,2), (3,1)定义的线和由(0,2), (2,-1), (4,1)定义的线。

我不需要确定交叉点的位置,但我需要一种有效的方法,因为我可以拥有非常多的边。我使用下面的代码来确定两个段是否相交,但对于较大长度的行来说这是低效的。此外,线条是图形中的边缘,它们被约束到已知的最大长度。

static bool IsOnSegment(float xi, float yi, float xj, float yj,
                    float xk, float yk) {
  return (xi <= xk || xj <= xk) && (xk <= xi || xk <= xj) &&
     (yi <= yk || yj <= yk) && (yk <= yi || yk <= yj);
}

static char ComputeDirection(float xi, float yi, float xj, float yj,
                         float xk, float yk) {
  float a = (xk - xi) * (yj - yi);
  float b = (xj - xi) * (yk - yi);
  return a < b ? -1 : a > b ? 1 : 0;
}

// Do line segments (x1, y1)--(x2, y2) and (x3, y3)--(x4, y4) intersect? /
bool DoLineSegmentsIntersect(float x1, float y1, float x2, float y2,
                         float x3, float y3, float x4, float y4) {
char d1 = ComputeDirection(x3, y3, x4, y4, x1, y1);
char d2 = ComputeDirection(x3, y3, x4, y4, x2, y2);
char d3 = ComputeDirection(x1, y1, x2, y2, x3, y3);
char d4 = ComputeDirection(x1, y1, x2, y2, x4, y4);
return (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) &&
      ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) ||
     (d1 == 0 && IsOnSegment(x3, y3, x4, y4, x1, y1)) ||
     (d2 == 0 && IsOnSegment(x3, y3, x4, y4, x2, y2)) ||
     (d3 == 0 && IsOnSegment(x1, y1, x2, y2, x3, y3)) ||
     (d4 == 0 && IsOnSegment(x1, y1, x2, y2, x4, y4));
}

1 个答案:

答案 0 :(得分:0)

您会稍微更改Bentley-Ottmann Algorithm,以计算k时间和O((n+k)logn)空间中的所有O(n+k)个交叉点。

建议修改 - Bentley-Ottmann算法获取一组段并报告所有交叉点。您可以将分段线分割为一组段,并将此集作为算法的输入。找到某个交叉点时,只检查交叉段是否属于同一个分段线。如果不是,则意味着您刚刚找到了分段线的交叉点。

请注意,在最坏的情况下,当交叉点数量非常大时,复杂度将为O(n^2)。对你来说最糟糕的情况是两条分段线,看起来像交织在一起的蛇。请记住,至少有O(N)个伪交叉点 - 每个分段线将产生O(length)伪交叉点,并且lenght1 + lenght2 = N,其中N是段的总数,有O(N)伪交叉。伪交叉是这样的交集,将由Bentley-Ottmann算法检测,但不应被考虑在内。

实施提示 - 基于扫描线算法的Bentley-Ottmann算法,该算法基于分选点 - (X,Y)对。现在你应该排序三元组(X,Y,segmentsLineID)。在你的情况下,所有三元组将是(X,Y,1)或(X,Y,2)