2014-10-31 128 views
2

我已經搜索範圍很廣,但尚未找到適合此問題的答案。給定球體上的兩條線,每條線都由它們的起點和終點確定,以確定它們是否相交以及它們在哪裏相交。我發現這個網站(http://mathforum.org/library/drmath/view/62205.html)通過一個很好的算法來處理兩個大圓的交點,儘管我堅持確定給定的點是否位於大圓的有限部分。球體表面上的測地線(最短距離路徑)之間的交點

我發現幾個網站聲稱他們已經實施了這個,包括一些問題在這裏和stackexchange,但他們似乎總是減少回到兩個大圓的交集。

蟒蛇類我寫如下,似乎差不多的工作:

class Geodesic(Boundary): 
    def _SecondaryInitialization(self): 
    self.theta_1 = self.point1.theta 
    self.theta_2 = self.point2.theta 
    self.phi_1 = self.point1.phi 
    self.phi_2 = self.point2.phi 

    sines = math.sin(self.phi_1) * math.sin(self.phi_2) 
    cosines = math.cos(self.phi_1) * math.cos(self.phi_2) 
    self.d = math.acos(sines - cosines * math.cos(self.theta_2 - self.theta_1)) 

    self.x_1 = math.cos(self.theta_1) * math.cos(self.phi_1) 
    self.x_2 = math.cos(self.theta_2) * math.cos(self.phi_2) 
    self.y_1 = math.sin(self.theta_1) * math.cos(self.phi_1) 
    self.y_2 = math.sin(self.theta_2) * math.cos(self.phi_2) 
    self.z_1 = math.sin(self.phi_1) 
    self.z_2 = math.sin(self.phi_2) 

    self.theta_wraps = (self.theta_2 - self.theta_1 > PI) 
    self.phi_wraps = ((self.phi_1 < self.GetParametrizedCoords(0.01).phi and 
     self.phi_2 < self.GetParametrizedCoords(0.99).phi) or (
     self.phi_1 > self.GetParametrizedCoords(0.01).phi) and 
     self.phi_2 > self.GetParametrizedCoords(0.99)) 

    def Intersects(self, boundary): 
    A = self.y_1 * self.z_2 - self.z_1 * self.y_2 
    B = self.z_1 * self.x_2 - self.x_1 * self.z_2 
    C = self.x_1 * self.y_2 - self.y_1 * self.x_2 
    D = boundary.y_1 * boundary.z_2 - boundary.z_1 * boundary.y_2 
    E = boundary.z_1 * boundary.x_2 - boundary.x_1 * boundary.z_2 
    F = boundary.x_1 * boundary.y_2 - boundary.y_1 * boundary.x_2 

    try: 
     z = 1/math.sqrt(((B * F - C * E) ** 2/(A * E - B * D) ** 2) 
      + ((A * F - C * D) ** 2/(B * D - A * E) ** 2) + 1) 
    except ZeroDivisionError: 
     return self._DealWithZeroZ(A, B, C, D, E, F, boundary) 

    x = ((B * F - C * E)/(A * E - B * D)) * z 
    y = ((A * F - C * D)/(B * D - A * E)) * z 

    theta = math.atan2(y, x) 
    phi = math.atan2(z, math.sqrt(x ** 2 + y ** 2)) 

    if self._Contains(theta, phi): 
     return point.SPoint(theta, phi) 

    theta = (theta + 2* PI) % (2 * PI) - PI 
    phi = -phi 

    if self._Contains(theta, phi): 
     return spoint.SPoint(theta, phi) 

    return None 

    def _Contains(self, theta, phi): 
    contains_theta = False 
    contains_phi = False 

    if self.theta_wraps: 
     contains_theta = theta > self.theta_2 or theta < self.theta_1 
    else: 
     contains_theta = theta > self.theta_1 and theta < self.theta_2 

    phi_wrap_param = self._PhiWrapParam() 
    if phi_wrap_param <= 1.0 and phi_wrap_param >= 0.0: 
     extreme_phi = self.GetParametrizedCoords(phi_wrap_param).phi 
     if extreme_phi < self.phi_1: 
     contains_phi = (phi < max(self.phi_1, self.phi_2) and 
      phi > extreme_phi) 
     else: 
     contains_phi = (phi > min(self.phi_1, self.phi_2) and 
      phi < extreme_phi) 
    else: 
     contains_phi = (phi > min(self.phi_1, self.phi_2) and 
      phi < max(self.phi_1, self.phi_2)) 

    return contains_phi and contains_theta 

    def _PhiWrapParam(self): 
    a = math.sin(self.d) 
    b = math.cos(self.d) 
    c = math.sin(self.phi_2)/math.sin(self.phi_1) 
    param = math.atan2(c - b, a)/self.d 

    return param 

    def _DealWithZeroZ(self, A, B, C, D, E, F, boundary): 
    if (A - D) is 0: 
     y = 0 
     x = 1 
    elif (E - B) is 0: 
     y = 1 
     x = 0 
    else: 
     y = 1/math.sqrt(((E - B)/(A - D)) ** 2 + 1) 
     x = ((E - B)/(A - D)) * y 

    theta = (math.atan2(y, x) + PI) % (2 * PI) - PI 
    return point.SPoint(theta, 0) 

def GetParametrizedCoords(self, param_value): 
    A = math.sin((1 - param_value) * self.d)/math.sin(self.d) 
    B = math.sin(param_value * self.d)/math.sin(self.d) 

    x = A * math.cos(self.phi_1) * math.cos(self.theta_1) + (
    B * math.cos(self.phi_2) * math.cos(self.theta_2)) 
    y = A * math.cos(self.phi_1) * math.sin(self.theta_1) + (
     B * math.cos(self.phi_2) * math.sin(self.theta_2)) 
    z = A * math.sin(self.phi_1) + B * math.sin(self.phi_2) 

    new_phi = math.atan2(z, math.sqrt(x**2 + y**2)) 
    new_theta = math.atan2(y, x) 

    return point.SPoint(new_theta, new_phi) 

編輯:我忘了指定,如果兩條曲線決心相交,然後我需要有點路口。

回答

5

一個更簡單的方法是用幾何圖元操作來表示問題,如dot product,cross producttriple product。的ü的行列式的符號,v瓦特告訴你它通過v跨越W¯¯平面的一側包含ü。這使我們能夠檢測何時兩個點位於飛機的相對位置上。這相當於測試一個大圓圈是否跨越另一個大圓圈。執行這個測試兩次告訴我們兩個大圓段是否相互交叉。

該實現不需要三角函數,不需要除法,不需要與pi進行比較,也不需要圍繞極點進行特殊的操作!

class Vector: 
    def __init__(self, x, y, z): 
     self.x = x 
     self.y = y 
     self.z = z 

def dot(v1, v2): 
    return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z 

def cross(v1, v2): 
    return Vector(v1.y * v2.z - v1.z * v2.y, 
        v1.z * v2.x - v1.x * v2.z, 
        v1.x * v2.y - v1.y * v2.x) 

def det(v1, v2, v3): 
    return dot(v1, cross(v2, v3)) 

class Pair: 
    def __init__(self, v1, v2): 
     self.v1 = v1 
     self.v2 = v2 

# Returns True if the great circle segment determined by s 
# straddles the great circle determined by l 
def straddles(s, l): 
    return det(s.v1, l.v1, l.v2) * det(s.v2, l.v1, l.v2) < 0 

# Returns True if the great circle segments determined by a and b 
# cross each other 
def intersects(a, b): 
    return straddles(a, b) and straddles(b, a) 

# Test. Note that we don't need to normalize the vectors. 
print(intersects(Pair(Vector(1, 0, 1), Vector(-1, 0, 1)), 
       Pair(Vector(0, 1, 1), Vector(0, -1, 1)))) 

如果你想在角度theta和披方面的初始化單位向量,就可以做到這一點,但我建議立即向笛卡爾轉換(X,Y,Z)座標來執行所有後續計算。

+0

這是一個奇妙和優雅的解決方案,是否兩條線相交。我忘了說明,在兩者相交的情況下,我也需要交點。例如,上面的Intersects方法可以工作,但是它具有多重性,因爲它可以計算以該段爲特徵的大圓的交點,這減少了我對檢測到的交點是否存在於段上的檢查不足。 – Jordan 2014-11-01 20:23:03

+0

交叉點位於兩個平面上,所以它應該是矢量'm = cross(cross(a.v1,a.v2),cross(b.v1,b.v2))'的某個實數倍。唯一的問題是交點是否是'm'或'-m'的標準化。我認爲你可以通過計算任意三個點的行列式的符號來獲得,例如'det(a.v1,b.v1,b.v2)'。 – 2014-11-01 20:34:45

+0

哇,那麼優雅! – ZpaceZombor 2017-04-28 12:42:28