试图弄清楚两个线段是否相交

时间:2013-11-18 16:05:48

标签: java algorithm line-segment

我有一个由两个点p1和p2组成的线段,以及一个由点p3和p4组成的第二个线段。我想弄清楚它们是否相交,到目前为止,我没有运气。到目前为止,这是我的代码:

public static double angle(Point p1, Point p2, Point p3) {
    double AB = length(p2, p1);
    double BC = length(p2, p3);
    double AC = length(p3, p1);
    return Math.acos((sqr(BC) + sqr(AB) - sqr(AC)) / (2 * BC * AB)) * (180 / Math.PI);
}

public static boolean doIntersect(Point p1, Point p2, Point p3, Point p4) {
    double a = angle(p4, p3, p2);
    double b = angle(p3, p2, p1);
    double c = 180 - b - a;

    System.out.println("a: " + a + ", b: " + b + ", c:" + c);

    if((length(p3, p2) * Math.sin(a)) / Math.sin(c) > length(p2, p1)) return false;
    if((length(p3, p2) * Math.sin(b)) / Math.sin(c) > length(p3, p4)) return false;
    return true;
}

public static double length(Point point1, Point point2) {
    return Math.sqrt(sqr(point1.x - point2.x) + sqr(point1.y - point2.y));
}

public static double sqr(double doub) {
    return Math.pow(doub, 2);
}

但这不起作用。有时,角度“c”甚至出现为负数。

此外,Point是一个带有两个参数的自定义类:x和y。应该是相当不言自明的。

3 个答案:

答案 0 :(得分:0)

我建议首先提出一个数学解决方案,验证它,然后尝试将其迁移到计算机程序。

答案 1 :(得分:0)

有两种解决方案,每种解决方案都有自己的优势:

1。数学解决方案

从两个点p1和p2,可以很容易地得到跨越这两个点的线的公式y = m*x + b。对于由(p1,p2)和(p3,p4)定义的线的公式,通过均衡再次计算它们的交点也很容易。剩下要做的就是检查交叉点是否是两个线段的一部分,这是相当明显的。

2。算法解决方案

另一种方法是应用sweep line algorithm:按各自的x坐标对所有点进行排序。然后,对于x顺序中的每个点,想象一条从点到点跳跃的垂直线。

如果在从线段A访问所有点后到达线段B的第一个点,则没有交叉点。

否则,您的第一个点来自线段A,而您的第二个点来自线段B.因此,您的第三个和第四个点分别属于A和B或B和A.将注意力集中在y坐标上,并自行解决该解决方案的最后部分。 ; - )

编辑:或者更简单,在line segment intersection上询问维基百科。

答案 2 :(得分:0)

对于角度计算,不是使用复杂的方法来求和x和y差的平方,而是取和的平方根,然后将该值平方,然后使用acos,可以使用atan2从x和y差异计算适当象限的角度。这比acos方法更快,问题更少。

但是,根本不需要计算任何角度。相反,首先测试重叠是否不可行。也就是说,如果No为false,则返回((x₁≤x₃≤x₂ || x₁≤x₄≤x₂ || x₃≤x₁≤x₄ || x₃≤x₂≤x₄) && (y≤y₃≤y₂ || y₁≤y₄≤y₂ || y₃≤y₁≤y₄ || y₃≤y₂≤y₄))。 (设p 1,p 2是一个段的端点,另一个是p 3,p 4端点。)

如果情况不可行,则可以通过查看三角形p 1,p 2,p 3和p 1,p 2,p 4区域的符号是否不同来测试是否发生交叉。 p 1,p 2,p 3的面积是±(x 1·y 2-x 2·y 1 + x 3·(y 2-y 1)+ y 3·(x 2-x 1))/ 2,因为它可以忽略因子1/2不影响标志。也就是说,对于可行的情况,您可以计算
t = x₁·y₂-x₂·y₁
u = t + x₃·(y₂-y₁) + y₃·(x₂-x₁)
v = t + x₄·(y₂-y₁) + y₄·(x₂-x₁)
如果Yes,则返回u·v ≤ 0,否则返回No。使用这样的方法的关键是避免必须检查垂直或水平线,平行线或重合线等产生的所有特殊情况。如果您希望出现退化线,请报告Yes if {{ 1}}或u·v < 0 if No,否则测试一行是否在另一行有一个端点。