使用Numpy

时间:2018-12-09 10:39:46

标签: python arrays numpy scipy computational-geometry

假设我有一个n 3D点列表,这些列表存储在形状为(3, n)的Numpy数组中。我想在该列表中找到所有4个点的集合,以使4个点共面。我该怎么办?

例如,给定包含points的数组,其中包含在3D空间中绕任意角度旋转的立方体的8个顶点(无特定顺序):

points = np.array([[ 0.8660254 ,  0.8660254 ,  0.        ,  0.3660254 , -0.5       ,  0.3660254 ,  0.        , -0.5       ],
                   [ 0.35355339, -0.35355339,  0.70710678, -0.25881905,  0.09473435, -0.96592583,  0.        , -0.61237244],
                   [ 1.06066017,  0.35355339,  0.70710678,  1.67303261,  1.31947922,  0.96592583,  0.        ,  0.61237244]])

我如何找到位于立方体每个面角上的6组4个顶点?具体来说,我正在寻找一种完全矢量化的基于Numpy / Scipy的解决方案。

编辑:正如ShlomiF指出的那样,一个立方体的顶点实际上有12个共面集合,包括沿着该立方体的对角线位于平面上的这些顶点。

这是我用来生成points的代码:

import numpy as np
import scipy.linalg as spl

def rot(axis, theta):
    return spl.expm(np.cross(np.eye(len(axis)), axis/spl.norm(axis)*theta))

rot3 = rot((1,0,0), np.pi/4) @ rot((0,1,0), np.pi/3) @ rot((0,0,1), np.pi/2)

points = np.array([[1, 0, 1, 1, 1, 0, 0, 0],
                   [0, 0, 0, 1, 1, 1, 0, 1],
                   [1, 1, 0, 1, 0, 1, 0, 0]])

points = rot3 @ points

2 个答案:

答案 0 :(得分:1)

以下内容可能不是非常快速的解决方案,但它可以工作并具有数学/几何意义。
但首先-请注意,由于“对角线”平面穿过您的立方体,因此您的示例具有4个共面点的 12 个子集,而不是8个。可以将其形式化,但应保持原样(如果不通过注释,请告诉我)。
用我们的方式,最简单的方法是生成大小为4的所有子集(不重复进行重新排序),然后检查这4个点定义的体积是否为0;也就是说,这4个点中的任何3个都定义了包含第4个点的平面。 (此方法在很多堆栈交换问题中都有介绍,并且在the wolfram definition of "Coplanar"中也有介绍)。

可以很简单地完成此操作,如下所示:

import numpy as np
import scipy.linalg as spl
from itertools import combinations

def rot(axis, theta):
    return spl.expm(np.cross(np.eye(len(axis)), axis/spl.norm(axis)*theta))

rot3 = rot((1,0,0), np.pi/4) @ rot((0,1,0), np.pi/3) @ rot((0,0,1), np.pi/2)

points = np.array([[1, 0, 1, 1, 1, 0, 0, 0],
                   [0, 0, 0, 1, 1, 1, 0, 1],
                   [1, 1, 0, 1, 0, 1, 0, 0]])

points = rot3 @ points

subsets_of_4_points = list(combinations(points.T, 4)) # 70 subsets. 8 choose 4 is 70.
coplanar_points = [p for p in subsets_of_4_points if np.abs(np.linalg.det(np.vstack([np.stack(p).T, np.ones((1, 4))]))) < 0.000001]  # due to precision stuff, you cant just do "det(thing) == 0"

您将获得全部12组4组共面点。

通过以下简单代码获得的点的简单可视化(从最后一个片段开始,带有额外的导入):

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# Get pairs of points for plotting the lines of the cube:
all_pairs_of_points = list(combinations(points.T, 2))

# Keep only points with distance equal to 1, to avoid drawing diagonals:
neighbouring_points = [list(zip(list(p1), list(p2))) for p1, p2 in all_pairs_of_points if np.abs(np.sqrt(np.sum((p1 - p2)**2)) - 1) < 0.0001]

plt.figure()
for i in range(12):

    ax3d = plt.subplot(3, 4, i+1, projection='3d')

    # Draw cube:
    for point_pair in neighbouring_points:
        ax3d.plot(point_pair[0], point_pair[1], point_pair[2], 'k')

    # Choose coplanar set:    
    p = coplanar_points[i]

    # Draw set:
    for x, y, z in p:
        ax3d.scatter(x, y, z, s=30, c='m')
    ax3d.set_xticks([])
    ax3d.set_yticks([])
    ax3d.set_zticks([])

plt.suptitle('Coplanar sets of 4 points of the rotated 3D cube')

这将产生以下可视化效果(再次针对此特定示例):

12 co-planar points

希望有帮助。
祝你好运!

答案 1 :(得分:0)

有四个点的70个子集,您需要计算它们形成的四面体的体积。如果您的形状足够接近立方体,则共面集将是体积最小的十二个集。

对于任意体积,您还可以比较通过将体积除以四个最大脸的面积而获得的高度。这将

n.(n-1).(n-2).(n-3) / 4!

体积计算和面积计算的四倍。

穷举法将是可怕的(O(n ^ 4)!)。而且矢量化将要求在几何计算正确之前准备所有顶点组合。