简体   繁体   English

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

[英]Get the surface area of a polyhedron (3D object)

I have a 3D surface, (think about the xy plane).我有一个 3D 表面,(想想 xy 平面)。 The plane can be slanted.飞机可以倾斜。 (think about a slope road). (想想斜坡路)。

Given a list of 3D coordinates that define the surface( Point3D1X , Point3D1Y , Point3D1Z , Point3D12X , Point3D2Y , Point3D2Z , Point3D3X , Point3D3Y , Point3D3Z , and so on), how to calculate the area of the surface?给定定义表面的 3D 坐标列表( Point3D1XPoint3D1YPoint3D1ZPoint3D12XPoint3D2YPoint3D2ZPoint3D3XPoint3D3YPoint3D3Z等),如何计算表面的面积?

Note that my question here is analogous to finding area in 2D plane.请注意,我在这里的问题类似于在 2D 平面中查找区域。 In 2D plane we have a list of points that defines a polygon, and using this list of points we can find the area of the polygon.在 2D 平面中,我们有一个定义多边形的点列表,使用这个点列表我们可以找到多边形的面积。 Now assuming that all these points have z values in such a way that they are elevated in 3D to form a surface.现在假设所有这些点都具有z值,它们在 3D 中被提升以形成一个表面。 My question is how to find the area of that 3D surface?我的问题是如何找到那个 3D 表面的面积?

Since you say it's a polyhedron, stacker's link (既然你说是多面体,那么stacker的链接( http://softsurfer.com/Archive/algorithm_0101/algorithm_0101.htm ) is applicable. http://softsurfer.com/Archive/algorithm_0101/algorithm_0101.htm ) 适用。

Here's my approximate C# translation of the C code for your situation:这是我针对您的情况对 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;
}

I upvoted a few answers which I think are correct.我赞成一些我认为正确的答案 But I think the simplest way to do it-- regardless of whether it's in 2D or 3D, is to use the following formula:但我认为最简单的方法——无论是 2D 还是 3D,都是使用以下公式:

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

Where × is the vector cross .其中×向量交叉

The code to do this is:执行此操作的代码是:

    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);
    }

Note that the solution doesn't depend on projection to x-plane, which I think is clunky.请注意,该解决方案不依赖于 x 平面的投影,我认为这很笨拙。

What do you think?你怎么认为?

@Graviton I can't comment on the answer above so I will submit a new one. @Graviton 我无法对上面的答案发表评论,所以我将提交一个新答案。

This might be my unfamiliarity with c# syntax, but I believe your answer is missing the dot product with the unit normal vector.这可能是我对 c# 语法不熟悉,但我相信您的答案是缺少单位法向量的点积。 The formula should be:公式应该是:

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

where n refers to unit normal vector to the plane, .其中n是指平面的单位法向量, . to dot product and x cross product.点积和x交叉积。

The normal can be calculated using any 3 vectors of the polygon:可以使用多边形的任意 3 个向量计算法线:

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

Here's a javascript implementation using the Vector.js lib:这是使用 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;
  };

You can derive the solution in terms of the 2D solution.您可以根据 2D 解决方案推导出解决方案。

Consider the polygon made up from a heap of smaller triangles.考虑由一堆较小的三角形组成的多边形。

Project each triangle back to the XY plane.将每个三角形投影回 XY 平面。 You can show that the area of the orignal triangle is 1/(nk) times the area of the projected triangle.您可以证明原始三角形的面积是投影三角形面积的 1/(nk) 倍。 (Here n is the unit normal to the plane containing the polygon, and k is the unit vector in the z direction) (这里n是包含多边形的平面的法线单位,k是z方向的单位向量)

So the total area of the original is 1/(nk) times the area of the polygon projected into the XY plane.所以原始的总面积是投影到 XY 平面的多边形面积的 1/(nk) 倍。 Which you can work out using your existing 2D formula.您可以使用现有的 2D 公式计算出来。

You can calculate n by (e1 x e2 ) / ||您可以通过 (e1 x e2 ) / || 计算 n e1 x e2 || e1 x e2 || where e1 and e2 are any 2 non-parallel edges of your polygon.其中 e1 和 e2 是多边形的任意 2 个非平行边。

Of course you may get better (more accurate) results by projecting into the XZ or YZ plane.. you should pick the one with the normal closest to that of your plane.当然,通过投影到 XZ 或 YZ 平面,您可能会获得更好(更准确)的结果。您应该选择法线最接近您平面的法线。

I don't know about optimising this method (I've not done it in code before), but the way to approach it mathematically is to split your shape into triangles, the area of which is then easily calculated and summed.我不知道如何优化这种方法(我之前没有在代码中完成过),但是从数学上讲它的方法是将你的形状分成三角形,然后很容易计算和求和它的面积。 (Remember: area of a triangle is width * height * 0.5 - you'll need to calculate the height of non-right-angled triangles.) (请记住:三角形的面积是宽度 * 高度 * 0.5 - 您需要计算非直角三角形的高度。)

Doing these things in 3D generally means one more calculation is needed at each stage.在 3D 中做这些事情通常意味着在每个阶段都需要进行更多的计算。 For example, in 2D, the distance between 2 points (the length of a side of your shape) is calculated something like this (pseduocode because I don't have VS on this machine):例如,在 2D 中,2 个点之间的距离(形状的一边的长度)的计算方式如下(伪代码,因为我在这台机器上没有 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);
}

In three dimensions that becomes:在三个维度上变成:

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);
}

Splitting a shape into arbitrary triangles just involves picking any three adjacent vertices at a time, until your're down to your last three.将一个形状分割成任意三角形只需要一次选择任意三个相邻的顶点,直到最后三个顶点。

Another solution that won't require you to create a mesh of polygons is to do a contour integral around the perimeter.另一个不需要您创建多边形网格的解决方案是围绕周边进行轮廓积分。 You use Green's theorem to convert the area integral into a contour integral, then use something simple like Gauss quadrature to integrate and sum each contribution.您使用格林定理将面积积分转换为等高线积分,然后使用高斯求积之类的简单方法对每个贡献进行积分和求和。 You do have to have a definition of the perimeter.你必须有周长的定义。

This process can work with 2D shapes that have holes as well.此过程也适用于有孔的 2D 形状。 You just have to define a cut that runs from the outer perimeter to the hole, integrate around the hole, and then go back out to the perimeter.您只需要定义一个从外周边延伸到孔的切口,围绕孔进行积分,然后再返回到周边。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM