Python中nD线与凸包的交点

use*_*814 14 python intersection convex-hull computational-geometry

我使用scipy.spatial.ConvexHull创建了一个凸包.我需要计算凸包和光线之间的交点,从0开始并在某个其他定义点的方向上.已知凸包含0,因此应保证交叉.问题的维度可以在2到5之间变化.我尝试了一些谷歌搜索,但没有找到答案.我希望这是计算几何中已知解决方案的常见问题.谢谢.

B. *_* M. 6

根据qhull.org,x凸包的一个方面的点验证V.x+b=0,在哪里Vb给出hull.equations.(.这里代表点积.V是长度为1的法线向量.)

如果V是法线,b是偏移量,x是凸包内部的点,那么Vx + b <0.

如果U是射线的矢量,则射线O的方程式为x=?U, ?>0.所以射线的一个方面是 x = ?U = -b/(V.U) U.与船体的唯一交叉点对应于正值的最小值?: 在此输入图像描述

下一个代码给出了它:

import numpy as np
from scipy.spatial import ConvexHull

def hit(U,hull):
    eq=hull.equations.T
    V,b=eq[:-1],eq[-1]
    alpha=-b/np.dot(V,U)
    return np.min(alpha[alpha>0])*U
Run Code Online (Sandbox Code Playgroud)

这是一个纯粹的numpy解决方案,所以它很快.[-1,1]^3立方体中100万个点的示例:

In [13]: points=2*np.random.rand(1e6,3)-1;hull=ConvexHull(points)

In [14]: %timeit x=hit(np.ones(3),hull) 
#array([ 0.98388702,  0.98388702,  0.98388702])
10000 loops, best of 3: 30 µs per loop
Run Code Online (Sandbox Code Playgroud)


sam*_*gak 5

正如 Ante 在评论中提到的,您需要找到船体中所有线/平面/超平面的最近交点。

要找到光线与超平面的交点,请对归一化光线与超平面法线进行点积,这将告诉您沿光线的每个单位距离在超平面法线的方向上移动了多远。

如果点积为负,则表示超平面与光线方向相反,如果为零,则表示光线与其平行且不会相交。

一旦你有一个正的点积,你就可以计算出超平面在射线方向上的距离,方法是将平面在平面法线方向上的距离除以点积。例如,如果平面相距 3 个单位,而点积为 0.5,那么沿射线移动的每个单位只会靠近 0.5 个单位,因此超平面在射线方向上的距离为 3 / 0.5 = 6 个单位.

一旦你计算了所有超平面的这个距离并找到了最近的一个,交点就是射线乘以最近的距离。

这是 Python 中的解决方案(normalize 函数来自此处):

def normalize(v):
    norm = np.linalg.norm(v)
    if norm == 0: 
       return v
    return v / norm

def find_hull_intersection(hull, ray_point):
    # normalise ray_point
    unit_ray = normalize(ray_point)
    # find the closest line/plane/hyperplane in the hull:
    closest_plane = None
    closest_plane_distance = 0
    for plane in hull.equations:
        normal = plane[:-1]
        distance = plane[-1]
        # if plane passes through the origin then return the origin
        if distance == 0:
            return np.multiply(ray_point, 0) # return n-dimensional zero vector 
        # if distance is negative then flip the sign of both the
        # normal and the distance:       
        if distance < 0:
            np.multiply(normal, -1);
            distance = distance * -1
        # find out how much we move along the plane normal for
        # every unit distance along the ray normal:
        dot_product = np.dot(normal, unit_ray)
        # check the dot product is positive, if not then the
        # plane is in the opposite direction to the rayL
        if dot_product > 0:  
            # calculate the distance of the plane
            # along the ray normal:          
            ray_distance = distance / dot_product
            # is this the closest so far:
            if closest_plane is None or ray_distance < closest_plane_distance:
                closest_plane = plane
                closest_plane_distance = ray_distance
    # was there no valid plane? (should never happen):
    if closest_plane is None:
        return None
    # return the point along the unit_ray of the closest plane,
    # which will be the intersection point
    return np.multiply(unit_ray, closest_plane_distance)
Run Code Online (Sandbox Code Playgroud)

二维测试代码(解决方案推广到更高维度):

from scipy.spatial import ConvexHull
import numpy as np

points = np.array([[-2, -2], [2, 0], [-1, 2]])
h = ConvexHull(points)
closest_point = find_hull_intersection(h, [1, -1])
print closest_point
Run Code Online (Sandbox Code Playgroud)

输出:

[ 0.66666667 -0.66666667]
Run Code Online (Sandbox Code Playgroud)