我为平面中两个线段的交叉编写了一个代码测试。我不会打扰你的所有细节。
代码采用两个线段,每个线段由两个端点描述,然后通过拟合a
中的b
和y = a*x + b
将每个线段拟合到一条线。然后它通过x = (b2 - b1) / (a2 - a1)
找到两条线的交点。最后,它测试交叉点x
是否包含在两个线段中。
相关部分如下:
# line parameterization by a = Delta y / Delta x, b = y - a*x
a1 = (line1.edge2.y - line1.edge1.y) / (line1.edge2.x - line1.edge1.x)
b1 = line1.edge1.y - a1 * line1.edge1.x
a2 = (line2.edge2.y - line2.edge1.y) / (line2.edge2.x - line2.edge1.x)
b2 = line2.edge1.y - a2 * line2.edge1.x
# The intersection's x
x = - (b2 - b1) / (a2 - a1)
# If the intersection x is within the interval of each segment
# then there is an intersection
if (isininterval(x, line1.edge1.x, line1.edge2.x) and
isininterval(x, line2.edge1.x, line2.edge2.x)):
return True
else:
return False
为简洁起见,我放弃了大量处理特定情况的测试,例如当边缘彼此平行时(a1==a2
),当它们在同一条线上时,当边缘长度为0时,边缘处于边缘时沿着垂直轴(然后a
变得无限)等。
函数isininterval
只是
def isininterval(x0, x1, x2):
"""Tests if x0 is in the interval x1 to x2"""
if x1 <= x0 <= x2 or x2 <= x0 <= x1:
return True
else:
return False
现在的问题是:我发现由于舍入误差,当交叉点与段边缘重合时,测试将给出错误的结果。
例如,在(0,0)和(3,5)之间的line1和(3,5)和(7,1)之间的line2,得到的交集x是2.9999999999999996
,这给出了错误的答案。应该是3。
你能建议一个解决方案吗?
答案 0 :(得分:5)
这是浮点运算的问题/特征。有一些方法可以通过某些方式对指令进行排序来最小化错误,但最后,您将获得近似答案,因为您可能使用有限位数表示无限数字。
您需要定义您构建的任何函数,使其能够容忍这些错误。看看你的例子,“正确”值和你得到的值之间的差异是1e-16
的顺序 - 非常低。
由于不平等,特别是平等,放松精确/双向匹配的约束是值得的。例如,如果您想测试x == 3
,则可以将其写为abs(x - 3) < EPSILON
,其中EPSILON = 1e-6
或EPSILON = 1e-9
。基本上,您想拥有的和您拥有的之间的差异小于非常小的值。同上,对于不平等,您可以测试3 - EPSILON <= x
或x <= 3 + EPSILON
。