【问题标题】:Ray-/segment-intersection test for parallelism and collinearity fails bcz of float accuracy in python用于并行性和共线性的射线/段相交测试在 python 中的浮点精度的 bcz 失败
【发布时间】:2018-02-12 13:40:26
【问题描述】:

我正在尝试按照 Gareth Rees 的出色说明实现一个函数来在 python 中查找射线/线段交叉点: https://stackoverflow.com/a/14318254/7235455https://stackoverflow.com/a/565282/7235455

这是我的功能:

from math import radians, sin, cos
import numpy as np

def find_intersection(point0, theta, point1, point2):
    # convert arguments to arrays:
    p = np.array(point0, dtype=np.float) # ray origin
    q = np.array(point1, dtype=np.float) # segment point 1
    q2 = np.array(point2, dtype=np.float) # segment point 2
    r = np.array((cos(theta),sin(theta))) # theta as vector (= ray as vector)

    s = q2 - q # vector from point1 to point2
    rxs = np.cross(r,s)
    qpxs = np.cross(q-p,s)
    qpxr = np.cross(q-p,r)
    t = qpxs/rxs
    u = qpxr/rxs

    if rxs == 0 and qpxr == 0:
        t0 = np.dot(q-p,r)/np.dot(r,r)
        t1 = np.dot(t0+s,r)/np.dot(r,r)
        return "collinear"
    elif rxs == 0 and qpxr != 0:
        return "parallel"
    elif rxs != 0 and 0 <= t and 0 <= u and u <= 1: # removed t <= 1 since ray is inifinte
        intersection = p+t*r
        return "intersection is {0}".format(intersection)
    else:
        return None

当有交叉路口时,该功能可以正常工作。但它不承认并行性或共线性,因为条件 rxs == 0 和 qpxr == 0 不(曾经?)满足。运行例如:

p0 = (0.0,0.0)
theta = radians(45.0)
p1 = (1.0,1.0) 
p2 = (3.0,3.0)

c = find_intersection(p0,theta,p1,p2)

返回无。在 if 块给出之前添加 rxs 和 qpxr 的打印语句

rxs =  2.22044604925e-16 qpxr =  -1.11022302463e-16

我的结论是,由于浮点问题,该函数无法捕获第一个 if 语句的条件。 2.22044604925e-16 和 -1.11022302463e-16 非常小,但不幸的是不完全是 0。我知道浮点数不能有二进制的精确表示。

我的结论是正确的还是我遗漏了什么?有什么想法可以避免这个问题吗? 非常感谢!

【问题讨论】:

    标签: python floating-point geometry floating-accuracy


    【解决方案1】:

    是的,您的结论是正确的,问题在于“并行”谓词的数值稳定性。

    您可以将结果与小数字进行比较(例如,eps=1.0E-9)。它的大小可能取决于坐标范围(请注意,叉积会产生两倍的三角形面积,因此将eps 归一化为MaxVecLen**2 看起来是合理的)。

    更复杂但更精确的选项 - 使用强大的几何谓词,如 these ones。也许用于计算几何的 Python/NumPy 库包含一些此类操作的实现。

    【讨论】:

    • 我花了一段时间才深入研究它。您与小数和标准化进行比较的想法似乎最实用。缺点是,它给出了一些错误的平行/共线,但我想我可以忍受。
    【解决方案2】:

    有一种简单而安全的方法可以解决这个问题。

    写出光线的隐式方程(S(X, Y) = a X + b Y + c = 0)。当您在函数S 中插入线段端点的坐标时,您会得到两个值,即S0S1。如果它们的符号相反,则射线的支撑线与线段之间存在交点。

    在这种情况下,沿线段的交点位置由参数的值给出,等于

    - S0 / (S1 - S0).
    

    此表达式具有始终可计算的属性(只要符号发生变化)并在[0, 1] 范围内。它允许安全地计算交点。

    要仅选择位于所需半线(射线)上的那些交点,您只需计算射线原点处S(Xo, Yo) 的符号即可。

    此过程不会检测平行或共线光线,但没关系。无论如何,它都会产生良好的结果。

    【讨论】:

      猜你喜欢
      • 2013-04-25
      • 2016-02-23
      • 1970-01-01
      • 2022-11-20
      • 1970-01-01
      • 1970-01-01
      • 2015-05-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多