并行性和共线性的Ray- / segment-intersection测试在python中失败了bcz的浮点精度

时间:2017-09-04 10:21:59

标签: python floating-point geometry floating-accuracy

我正在尝试实现一个函数,在Gareth Rees的伟大指令之后在python中找到光线/段交叉点: https://stackoverflow.com/a/14318254/7235455https://stackoverflow.com/a/565282/7235455

这是我的功能:

14:18:09.950 [DEBUG] [org.gradle.launcher.daemon.server.exec.ExecuteBuild] The daemon has finished executing the build.
14:18:10.013 [DEBUG] [org.gradle.launcher.daemon.client.DaemonClientInputForwarder] Dispatching close input message: org.gradle.launcher.daemon.protocol.CloseInput@de542e
14:18:10.013 [DEBUG] [org.gradle.launcher.daemon.client.DaemonClientConnection] thread 10: dispatching class org.gradle.launcher.daemon.protocol.CloseInput
14:18:10.013 [DEBUG] [org.gradle.launcher.daemon.client.DaemonClient] Received result Failure[value=org.gradle.initialization.ReportedException: org.gradle.internal.exceptions.LocationAwareException: A problem occurred configuring root project '_app_'.] from daemon DaemonInfo{pid=7464, address=[093d98c5-c3fb-4d52-8769-f354b16ed886 port:59558, addresses:[/127.0.0.1]], state=Idle, lastBusy=1504514729826, context=DefaultDaemonContext[uid=1fa87ce9-9184-48a6-9489-499fe27fffa0,javaHome=C:\Program Files (x86)\Java\jdk1.8.0_131,daemonRegistryDir=D:\Users\RMuthu\.gradle\daemon,pid=7464,idleTimeout=10800000,daemonOpts=-XX:+HeapDumpOnOutOfMemoryError,-Xmx1024m,-Dfile.encoding=windows-1252,-Duser.country=US,-Duser.language=en,-Duser.variant]} (build should be done).
14:18:10.013 [DEBUG] [org.gradle.launcher.daemon.client.DaemonClientConnection] thread 1: dispatching class org.gradle.launcher.daemon.protocol.Finished
14:18:10.013 [DEBUG] [org.gradle.launcher.daemon.client.DaemonClientConnection] thread 1: connection stop
Picked up _JAVA_OPTIONS: -Djava.net.preferIPv4Stack=true

Caused by: org.gradle.internal.resolve.ModuleVersionResolveException: Could not resolve org.pitest:pitest-command-line:1.1.10.
        at org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ErrorHandlingModuleComponentRepository$ErrorHandlingModuleComponentRepositoryAccess.resolveComponentMet
        at org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ComponentMetaDataResolveState.process(ComponentMetaDataResolveState.java:66)
        at org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ComponentMetaDataResolveState.resolve(ComponentMetaDataResolveState.java:58)
        at org.gradle.api.internal.artifacts.ivyservice.ivyresolve.RepositoryChainComponentMetaDataResolver.findBestMatch(RepositoryChainComponentMetaDataResolver.java:13
        at org.gradle.api.internal.artifacts.ivyservice.ivyresolve.RepositoryChainComponentMetaDataResolver.findBestMatch(RepositoryChainComponentMetaDataResolver.java:11
        at org.gradle.api.internal.artifacts.ivyservice.ivyresolve.RepositoryChainComponentMetaDataResolver.resolveModule(RepositoryChainComponentMetaDataResolver.java:89
        ... 20 more
Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.gradle.internal.resource.transport.http.AlwaysRedirectRedirectStrategy

当有交叉点时,该功能正常工作。但它不能识别并行性或共线性,因为条件rxs == 0和qpxr == 0不符合(永远?)。运行例如:

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

返回None。在if-block之前为rxs和qpxr添加print语句

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语句的条件。 2.22044604925e-16和-1.11022302463e-16非常小,但遗憾的是不完全是0.我理解浮点数不能用二进制表示。

我的结论是正确的还是我错过了什么?对于避免此问题的实施有什么想法吗? 非常感谢!

2 个答案:

答案 0 :(得分:0)

是的,你的结论是对的,问题在于&#34; parallel&#34;谓语。

您可以将结果与较小的数字进行比较(例如,eps=1.0E-9)。它的大小可能取决于坐标范围(请注意,交叉乘积会给出加倍的三角形区域,因此eps MaxVecLen**2的标准化看起来合理。)

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

答案 1 :(得分:0)

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

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

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

- S0 / (S1 - S0).

此表达式具有始终可计算的属性(如果符号发生更改)且范围为[0, 1]。它允许安全地计算交叉点。

要仅选择所需半线(光线)上的交点,只需在光线原点计算S(Xo, Yo)的符号。

此程序不会检测平行光线或共线光线,但无关紧要。无论如何,它会产生合理的结果。