计算多面体体积的通用公式

时间:2009-12-03 08:12:19

标签: math

给定顶点列表(v),连接顶点(e)的边列表以及连接边(s)的曲面列表,如何计算多面体的体积?

5 个答案:

答案 0 :(得分:8)

  1. 取多边形并将它们分成三角形。
  2. 考虑由每个三角形和任意点(原点)形成的四面体。
  3. 对这些四面体的签名体积求和。
  4. 注意:

    1. 只有从外面看三角形的CC或CCW顺序保持一致时才会有效。
    2. 四面体的有符号体积等于下列矩阵的行列式的1/6:
    3. [x1 x2 x3 x4]
      [y1 y2 y3 y4]
      [z1 z2 z3 z4]
      [1 1 1 1]

      其中列是verticies(x,y,z,1)的齐次坐标。

      即使形状没有通过减去该体积以及添加它来封闭原点,它仍然有效,但这取决于具有一致的排序。

      如果你不能保留顺序,你仍然可以找到一些方法将其分解为四面体,并将每个行列式的行列式的1/6绝对值加起来。

答案 1 :(得分:2)

与多边形类似,我们可以将其分成三角形并对区域求和,
你可以将一个多面体分成金字塔并将它们的体积相加。但我不确定为此实现算法有多难。

(我相信有一种数学方法/公式,比如使用向量和矩阵 我建议您在http://mathoverflow.net

上发布您的问题

答案 2 :(得分:1)

首先,通过绘制新边缘将每个面分成三角形。

现在看一个三角形,并假设它位于“上”表面(其中一些细节将在以后变得不重要)。查看三角形下方的体积,下至多面体下方的某个水平面。如果{h1,h2,h3}是三个点的高度,并且A是基部的面积,那么实体的体积将是A(h1 + h2 + h3)/ 3。现在我们必须为面添加这些实体的体积,并为面减去它们以获得多面体的体积。

与代数一起玩,你会发现多面体在水平面上方的高度无关紧要。平面可以在多面体上方,或通过它,结果仍然是正确的。

所以我们需要的是(1)计算基础面积的方法,以及(2)从“较低”面部告诉“上”面的方法。第一个很容易,如果你有点的笛卡尔坐标,第二个很容易,如果点是有序的,你可以结合它们,一石二鸟。假设每个面都有逆时针顺序的角点列表。然后,这些点在x-y平面上的投影对于上表面是逆时针方向,对于下表面是顺时针方向。如果你使用this method来计算基础面积,那么它对于一个上面会呈现正面,而对于一个上面则会为负,所以你可以将它们全部加在一起并得到答案。

那你怎么得到有序的角落列表?从一个三角形开始,选择一个排序,对于每个边缘,共享该边缘的邻居应该以相反的顺序列出这两个点。从邻居移动到邻居,直到每个三角形都有一个列表。如果多面体的体积变为负数,则只需乘以-1(这意味着您为第一个三角形选择了错误的顺序,并且多面体是由内到外的。)

修改: 我忘记了最好的部分!如果你检查代数是否要添加这些卷,你会看到许多术语被取消,特别是在将三角形组合回原始面时。我没有详细说明这一点,但看起来最终的结果可能是一个非常简单的功能。

答案 3 :(得分:1)

我以前做过这个,但我使用的表面网格总是有三角形面。如果网格具有非三角形刻面,则可以先将它们轻松分解为三角形刻面。然后我把它喂给TetGen以获得内部的四面体化。最后,我把四面体的所有卷都加起来了。 TetGen相当容易使用,并且是除CGAL之外唯一可以处理复杂网格的库。如果你不介意安装一个巨大的库并使用疯狂的模板,CGAL很容易使用。

答案 4 :(得分:0)

这是Python中的潜在实现。 任何人都可以检查一下是否正确? 我相信我错过了积分的排列,因为我的第二个测试(立方体)给出0.666而不是1.想法有人吗?

干杯 EL

class Simplex(object):
    '''
    Simplex
    '''


    def __init__(self,coordinates):
        '''
        Constructor
        '''

        if not len(coordinates) == 4:
            raise RuntimeError('You must provide only 4 coordinates!')

        self.coordinates = coordinates



    def volume(self):
        '''
        volume: Return volume of simplex. Formula from http://de.wikipedia.org/wiki/Tetraeder
        '''
        import numpy

        vA = numpy.array(self.coordinates[1]) - numpy.array(self.coordinates[0])
        vB = numpy.array(self.coordinates[2]) - numpy.array(self.coordinates[0])
        vC = numpy.array(self.coordinates[3]) - numpy.array(self.coordinates[0])

        return numpy.abs(numpy.dot(numpy.cross(vA,vB),vC)) / 6.0  


class Polyeder(object):

    def __init__(self,coordinates):
        '''
        Constructor
        '''

        if len(coordinates) < 4:
            raise RuntimeError('You must provide at least 4 coordinates!')

        self.coordinates = coordinates


    def volume(self):

        pivotCoordinate = self.coordinates[0]

        volumeSum = 0

        for i in xrange(1,len(self.coordinates)-3):

            newCoordinates = [pivotCoordinate]

            for j in xrange(i,i+3):
                newCoordinates.append(self.coordinates[j])

            simplex = Simplex(newCoordinates)
            volumeSum += simplex.volume()

        return volumeSum


coords = []

coords.append([0,0,0])
coords.append([1,0,0])
coords.append([0,1,0])
coords.append([0,0,1])

s = Simplex(coords)
print s.volume()

coords.append([0,1,1])
coords.append([1,0,1])
coords.append([1,1,0])
coords.append([1,1,1])

p = Polyeder(coords)
print p.volume()