S01E15:多于三个点的最佳平面

813 阅读1分钟

说明

在三维空间中,3 个点确定一个平面(3 个点不能共线)。但是有时,我们会得到多个点,进而反推出平面位置,比如知道了多边形的顶点,但是位置精确度不够高,需要根据所有点集找到最接近的平面时。

几何

最简单的算法就是,我们把这些点每三个当成一个三角形,逐个计算法线,最后把得到的法线加起来,平均一下,就得到了“最佳”法线。当然,这个计算还可以化简,化简过程我们不再推算,根据书本,化简后的公式如下:

代码

static func estimatePlane(from points:[simd_float3]) -> Plane? {
    if points.count < 3 {
        return nil
    }
    var normal = simd_float3.zero
    var position = simd_float3.zero
    // 从最后一个顶点开始,避免在循环中做 if 判断
    var second = points.last!
    for vector in points {
        // 边向量乘积相加
        normal.x += (second.z + vector.z) * (second.y - vector.y)
        normal.y += (second.x + vector.x) * (second.z - vector.z)
        normal.z += (second.y + vector.y) * (second.x - vector.x)
        // 下一个顶点
        second = vector
        
        // 顺便求中心点,做为平面位置
        position += (vector / Float(points.count))
    }
    if length_squared(normal) < 0.0001 {
        return nil
    }
    normal = normalize(normal)
    return Plane(position: position, normal: normal)
}

其他

这个方法好处是计算简单效率高,复杂度只需要 O(n)。缺点是对顶点的排列顺序有要求:顶点顺时针排列时,得到法线为正。当点集的排列呈下面这种交叉时,正负方向的法线会互相抵消,当完全对称时,法线为 0。 那么这种情况应该怎么处理呢?很难处理,一种是调整数组顺序再算一遍(但不保证一定管用),另一种只能是更换其他算法,比如最小二乘法等。