获取多面体(3D物体)的表面积

12

我有一个三维曲面(类比于xy平面),该曲面可以倾斜(类比于斜坡路面)。给定一组定义曲面的三维坐标点(Point3D1X, Point3D1Y, Point3D1Z, Point3D12X, Point3D2Y, Point3D2Z, Point3D3X, Point3D3Y, Point3D3Z等等),如何计算该曲面的面积?

需要注意的是,这个问题类比于在二维平面中求面积。在二维平面中,我们有一组定义多边形的点,使用这些点我们可以计算出该多边形的面积。现在假设所有这些点都有一个z值,以便将它们在三维空间中抬升形成一个曲面。我的问题是如何找到该三维曲面的面积?


2
一个平面有无限的面积。也许你不是指一个平面? - John Saunders
飞机通常在面积上是无限的。为了确定面积,您需要更好地定义形状。 - Gabe
@jmbr,只有3D线条具有参数化,曲面没有。我的意思是,我有一个曲面,它包含在一个平面中(可以倾斜)。 - Graviton
您希望在本地空间还是屏幕空间内处理区域?- 我假设您是想在本地空间内处理。 - zebrabox
@zebrabox,有什么区别?它们分别代表什么意思? - Graviton
显示剩余7条评论
7个回答

11

由于您说它是多面体,因此 stacker 提供的链接 (http://softsurfer.com/Archive/algorithm_0101/algorithm_0101.htm) 适用于您。

这是我对您情况的 C 代码的近似 C# 翻译:

// NOTE: The original code contained the following notice:
// ---------------------------------------
// Copyright 2000 softSurfer, 2012 Dan Sunday
// This code may be freely used and modified for any purpose
// providing that this copyright notice is included with it.
// iSurfer.org makes no warranty for this code, and cannot be held
// liable for any real or imagined damage resulting from its use.
// Users of this code must verify correctness for their application.
// ---------------------------------------
// area3D_Polygon(): computes the area of a 3D planar polygon
//    Input:  int n = the number of vertices in the polygon
//            Point[] V = an array of n+2 vertices in a plane
//                       with V[n]=V[0] and V[n+1]=V[1]
//            Point N = unit normal vector of the polygon's plane
//    Return: the (float) area of the polygon
static float
area3D_Polygon( int n, Point3D[] V, Point3D N )
{
    float area = 0;
    float an, ax, ay, az;  // abs value of normal and its coords
    int   coord;           // coord to ignore: 1=x, 2=y, 3=z
    int   i, j, k;         // loop indices

    // select largest abs coordinate to ignore for projection
    ax = (N.x>0 ? N.x : -N.x);     // abs x-coord
    ay = (N.y>0 ? N.y : -N.y);     // abs y-coord
    az = (N.z>0 ? N.z : -N.z);     // abs z-coord

    coord = 3;                     // ignore z-coord
    if (ax > ay) {
        if (ax > az) coord = 1;    // ignore x-coord
    }
    else if (ay > az) coord = 2;   // ignore y-coord

    // compute area of the 2D projection
    for (i=1, j=2, k=0; i<=n; i++, j++, k++)
        switch (coord) {
        case 1:
            area += (V[i].y * (V[j].z - V[k].z));
            continue;
        case 2:
            area += (V[i].x * (V[j].z - V[k].z));
            continue;
        case 3:
            area += (V[i].x * (V[j].y - V[k].y));
            continue;
        }

    // scale to get area before projection
    an = Math.Sqrt( ax*ax + ay*ay + az*az);  // length of normal vector
    switch (coord) {
    case 1:
        area *= (an / (2*ax));
        break;
    case 2:
        area *= (an / (2*ay));
        break;
    case 3:
        area *= (an / (2*az));
        break;
    }
    return area;
}

多面体*(多边形是2D) - Albert Renshaw
不需要计算an,长度为1。你也会得到负面的面积,所以返回abs(area)是正确的。 - Noob

4

我点赞了几个答案,我认为它们是正确的。但我认为无论是在二维还是三维中,最简单的方法是使用以下公式:

area = sum(V(i+1) × V(i))/2;

其中×代表向量叉积

实现此操作的代码如下:

    public double Area(List<Point3D> PtList)
    {

        int nPts = PtList.Count;
        Point3D a;
        int j = 0;

        for (int i = 0; i < nPts; ++i)
        {
            j = (i + 1) % nPts;
            a += Point3D.Cross(PtList[i], PtList[j]);
        }
        a /= 2;
        return Point3D.Distance(a,default(Point3D));
    }

    public static Point3D Cross(Point3D v0, Point3D v1)
    {
        return new Point3D(v0.Y * v1.Z - v0.Z * v1.Y,
            v0.Z * v1.X - v0.X * v1.Z,
            v0.X * v1.Y - v0.Y * v1.X);
    }

请注意,该解决方案不依赖于投影到x平面上,我认为这样做很笨重。
你怎么看?

其他解决方案(如我所发布的)每个点执行更少的数学运算(1次乘法,1次减法)。你的方法每个点要执行6次乘法和3次减法。如果你没有太多的点或不必经常执行此操作,请使用看起来更简单的方法。否则,如果有足够快的速度注意到差异,请使用更快的方法。 - Gabe
@gabe:你提出的解决方案需要投影,这一点我不太喜欢。不过除此之外,它还是不错的。 - Graviton
@Graviton:你确定这是正确的吗?我认为它不适用于一般情况。你正在对一系列点沿着每个三角形的面积求和。你是否假设这些点实际上构成了一个凸多边形?无论如何,我都看不出这样做如何能得到正确的表面积,因为(1)你会有重叠的三角形,(2)你会错过一些区域。 - Chris
如果您假设一个有序的点列表形成一个凸多边形,我猜您想要正确地进行三角剖分 - 在这种情况下,应该不使用 PtList[i],PtList[j],而是保持一个点(例如第一个点)不变。意思是:您不需要使用 i,并且计算局部面积将如下所示:Point3D.Cross(PtList[0], PtList[j]); - Chris
@Chris,这是二维区域公式的直接扩展。在二维中,等价公式并不真正关心您是凸还是凹。 - Graviton


1

我不知道如何优化这种方法(我以前没有写过代码),但数学上的方法是将您的形状分成三角形,然后很容易计算和求和它们的面积。(记住:三角形的面积是宽度*高度*0.5-您需要计算非直角三角形的高度。)

在3D中做这些事情通常意味着每个阶段需要进行更多的计算。例如,在2D中,两点之间的距离(您形状的一条边的长度)可以通过以下方式计算(因为我在这台机器上没有VS,所以使用伪代码):

double DistanceBetween(Point a, Point b)
{
   double dx = a.x - b.x;
   double dy = a.y - b.y;
   return SquareRoot(dx*dx + dy*dy);
}

在三维空间中,它变成了:
double DistanceBetween(Point3d a, Point3d b)
{
   double dx = a.x - b.x;
   double dy = a.y - b.y;
   double dz = a.z - b.z;
   return SquareRoot(dx*dx + dy*dy + dz*dz);
}

将一个形状分割成任意三角形只需要每次选择任意三个相邻的顶点,直到最后只剩下最后三个。

1

@Graviton,我无法对上面的答案进行评论,因此我将提交一个新的答案。

这可能是我对c#语法不熟悉,但我认为您的答案缺少与单位法向量的点积。公式应该是:

area = n.sum( V(i+1) x V(i) )/2;

其中n是指法向量,.表示点乘,x表示叉乘。

法向量可以使用多边形的任意三个向量进行计算:

n = (V1-V0)x(V2-V0)/magnitude((V1-V0)x(V2-V0))

这是一个使用Vector.js库的JavaScript实现:

  function getArea (vecs) {
    var area = 0;
    var vecs = [];
    var j = 0;
    var a = new Vector(0,0,0);

    for (var i = 0; i < vecs.length; i++) {
      j = (i + 1) % vecs.length;
      a = a.add( vecs[i].cross(vecs[j]) );
    }
    a = a.divide(2);
    var v1 = vecs[1].subtract(vecs[0]);
    var v2 = vecs[2].subtract(vecs[0]);
    var normal = v1.cross(v2);
    normal = normal.unit();
    // area = a.length()/10000; // convert to m2
    area = (normal.dot(a))/10000;
    return area;
  };

1

你可以用2D解决方案来推导出解决方法。

考虑由许多小三角形组成的多边形。

将每个三角形投影回XY平面。你可以证明原始三角形的面积是投影三角形面积的1/(n.k)倍。(这里,n是包含多边形的平面的单位法向量,k是指向z方向的单位向量)

因此,原始多边形的总面积是投影到XY平面中的多边形面积的1/(n.k)倍。你可以使用现有的2D公式计算出它。

你可以通过(e1 x e2 ) / || e1 x e2 ||来计算n,其中e1和e2是多边形的任意两条非平行边。

当然,你可以将其投影到XZ或YZ平面以获得更好(更准确)的结果。你应该选择法向量最接近你所在平面的那个平面。


你将如何将这个公式推广到点的列表中,而不仅仅是三个点? - Graviton

1

另一个解决方案,不需要您创建多边形网格,是在周长周围进行轮廓积分。 您可以使用 Green定理将面积积分转换为轮廓积分,然后使用像 Gauss积分这样简单的方法来积分和总结每个贡献。 您必须有周长的定义。

这个过程也适用于带有孔洞的二维图形。 您只需定义一条从外周到孔洞的切口,围绕孔洞进行积分,然后回到周长即可。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接