3D线段盒交叉口

时间:2010-07-30 22:02:29

标签: python 3d

寻找代码来检测3D段(不是线/光线)和3D盒子(不一定是立方体,但总是轴对齐)之间的交叉点。盒子是体素,因此它们有规则的间距。

已经有代码来查找分段/平面交叉点。理想情况下,我想找到一个有效的解决方案,使其适应矩形,重复3d框的每个面,然后迭代成千上万的段和框。

seg_start = array([x1,y1,z1])
seg_end = array([x2,y2,z2])
plane_point = array([x3,y3,z3])
plane_normal = array([x4,y4,z4])
u = seg_end - seg_start
w = seg_start - plane_point
D = dot(plane_normal,u)
N = -dot(plane_normal,w)
sI = N / D
if sI >= 0 and sI <= 1:
    return 1

3 个答案:

答案 0 :(得分:2)

首先,在if条件中你可能意味着and而不是or,否则它将永远返回true。其次,如果你只是测试是否有交叉点,你可以更快地完成它(没有浮动除法):

  • 您可以使用矢量数学确定每个平面的哪个“边”是任何给定点: side = dot(my_point - plane_point, plane_normal)
    现在,如果side为正,则my_point位于“飞机前”(即它位于法线所指向的一侧);如果是否定的话,那就是飞机的“后面”。如果side为零,则您的观点位于飞机上。
  • 您可以通过测试来检查您的线段是否与(无限)平面相交,以查看起点和终点是否在不同的一侧:

    start_side = dot(seg_start - plane_point, plane_normal)
    end_side = dot(seg_end - plane_point, plane_normal)
    return start_side * end_side
    #if < 0, both points lie on different sides, hence intersection
    #if = 0, at least one point lies on the plane
    #if > 0, both points lie on the same side, i.e. no intersection
    
  • 您也可以使用“侧面”检查来进行轴对齐的长方体交叉(实际上,这适用于任何平行线):

    • 将您的盒子视为一组六架飞机
    • 确保平面法线全部指向框中的“向外”或“向内”。我假设你正在使用“向外”
    • 对于任何位于你的盒子里面的点,它必须是“所有六架飞机后面”。如果不是,它就在盒子外面。
    • 对于与框相交的任何段,一个点必须位于其外部,一个位于内部。
    • 就是这样!

编辑:最后一点实际上是不正确的;正如你所说,即使两个端点位于外面,体素也可以相交。所以这不是整个解决方案 - 实际上,如果不计算交叉点,就无法做到这一点。但是,您仍然可以使用“边测试”作为早期拒绝机制,以便减少您需要执行的完整计算次数:如果两个点都在同一侧六个平面中的任何一个,没有交叉点。

就您的具体情况而言,似乎您正在尝试为某些给定的线段找到所有相交的体素?在这种情况下,使用Bresenham's之类的东西来明确计算路径可能会更好,而不是测试所有体素的交叉点......

答案 1 :(得分:0)

只是为了以足够的背景存档(上面的答案似乎不包括一些碰撞案例):

This question looks to be quite the same, with a good answer.

More detailed, and with a better pre-check here.

答案 2 :(得分:-1)

因为框是轴对齐的,所以您需要做的就是检查每个坐标中的间隔交点。

这是python中的一个例子,有一些测试。请注意,它对于N维是通用的,它与盒子交叉的算法相同:

def are_intervals_intersecting(a0, a1, b0, b1):
    '''
    @param a0: float
    @param a1: float
    @param b0: float
    @param b1: float
    '''
    if (a1 < a0):
        a1, a0 = a0, a1

    if (b1 < b0):
        b1, b0 = b0, b1

    # 6 conditions:

    # 1)
    #        a0 ---------- a1                              a0 < b0 and a1 < b0
    #                             b0 ---------- b1         (no intersection)

    # 2)
    #               a0 ---------- a1
    #                      b0 ---------- b1                (intersection)

    # 3)
    #               a0 ------------------------ a1
    #                      b0 ---------- b1                (intersection)

    # 4)
    #                      a0 ---------- a1         
    #               b0 ------------------------ b1         (intersection)

    # 5)
    #                             a0 ---------- a1         (intersection)
    #                      b0 ---------- b1

    # 6)
    #                                    a0 ---------- a1  b0 < a0 and b1 < a0         
    #               b0 ---------- b1                       (no intersection)

    if b0 < a0:
        # conditions 4, 5 and 6
        return a0 < b1 # conditions 4 and 5
    else:
        # conditions 1, 2 and 3
        return b0 < a1 # conditions 2 and 3


def is_segment_intersecting_box(P0, P1, B0, B1):
    '''
    @param P0: tuple(float)
    @param P1: tuple(float)
    @param B0: tuple(float)
    @param B1: tuple(float)
    '''
    for i in xrange(len(P0)):
        if not are_intervals_intersecting(P0[i], P1[i], B0[i], B1[i]):
            return False
    return True


if __name__ == '__main__':
    assert not is_segment_intersecting_box(
        (0.0, 0.0, 0.0), (1.0, 1.0, 1.0), (2.0, 2.0, 2.0), (3.0, 3.0, 3.0))

    assert not is_segment_intersecting_box(
        (0.0, 0.0, 0.0), (4.0, 1.0, 1.0), (2.0, 2.0, 2.0), (3.0, 3.0, 3.0))

    assert not is_segment_intersecting_box(
        (1.5, 1.5, 0.0), (4.0, 2.5, 1.0), (2.0, 2.0, 2.0), (3.0, 3.0, 3.0))

    assert is_segment_intersecting_box(
        (1.5, 1.5, 0.0), (4.0, 2.5, 2.5), (2.0, 2.0, 2.0), (3.0, 3.0, 3.0))

    assert is_segment_intersecting_box(
        (1.5, 1.5, 1.5), (2.5, 2.5, 2.5), (2.0, 2.0, 2.0), (3.0, 3.0, 3.0))

    assert is_segment_intersecting_box(
        (2.5, 2.5, 2.5), (2.6, 2.6, 2.6), (2.0, 2.0, 2.0), (3.0, 3.0, 3.0))

    assert is_segment_intersecting_box(
        (2.5, 2.5), (2.5, 3.5), (2.0, 2.0), (3.0, 3.0))

    print 'ok'