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

我有一个3D表面,(想想xy平面)。 飞机可以倾斜。 (想想一条斜坡路)。

给定定义曲面的3D坐标列表( Point3D1XPoint3D1YPoint3D1ZPoint3D12XPoint3D2YPoint3D2ZPoint3D3XPoint3D3YPoint3D3Z等),如何计算曲面的面积?

请注意,我的问题类似于在2D平面中查找区域。 在2D平面中,我们有一个定义多边形的点列表,使用这个点列表我们可以找到多边形的面积。 现在假设所有这些点都具有z值,使得它们在3D中boost以形成表面。 我的问题是如何找到3D表面的区域?

既然你说它是一个多面体,堆栈器的链接( 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 = (Nx>0 ? Nx : -Nx); // abs x-coord ay = (Ny>0 ? Ny : -Ny); // abs y-coord az = (Nz>0 ? Nz : -Nz); // 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还是3D,都是使用以下公式:

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

其中X是向量交叉 。

执行此操作的代码是:

  public double Area(List 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平面,我认为这是笨重的。

你怎么看?

我不知道如何优化这种方法(我之前没有在代码中完成它),但是数学方法是将形状分成三角形,然后可以很容易地计算和求和其面积。 (请记住:三角形的区域是宽度*高度* 0.5 – 您需要计算非直角三角形的高度。)

在3D中执行这些操作通常意味着在每个阶段都需要再进行一次计算。 例如,在2D中,2点之间的距离(形状的一边的长度)计算如下(pseduocode,因为我在这台机器上没有VS):

 double DistanceBetween(Point a, Point b) { double dx = ax - bx; double dy = ay - by; return SquareRoot(dx*dx + dy*dy); } 

在三个维度中变为:

 double DistanceBetween(Point3d a, Point3d b) { double dx = ax - bx; double dy = ay - by; double dz = az - bz; return SquareRoot(dx*dx + dy*dy + dz*dz); } 

将形状拆分为任意三角形只需要一次选取任意三个相邻顶点,直到最后三个为止。

您可以根据2D解决方案推导出解决方案。

考虑从一堆较小三角形中疯狂的多边形。

将每个三角形投影回XY平面。 您可以显示原始三角形的面积是投影三角形面积的1 /(nk)倍。 (这里n是与包含多边形的平面垂直的单位,k是z方向上的单位矢量)

因此原件的总面积是投影到XY平面中的多边形面积的1 /(nk)倍。 您可以使用现有的2D公式计算出来。

您可以通过(e1 x e2)/ ||计算n e1 x e2 || 其中e1和e2是多边形的任何2个非平行边。

当然,通过投影到XZ或YZ平面,您可以获得更好(更准确)的结果。您应该选择最接近平面的法线。

另一种不需要您创建多边形网格的解决方案是围绕周边进行轮廓积分。 您使用格林定理将区域积分转换为轮廓积分,然后使用像高斯积分这样的简单积分并对每个贡献求和。 你必须有一个周界的定义。

此过程也适用于具有孔的2D形状。 您只需定义从外周到孔的切口,围绕孔整合,然后返回到周边。

@Graviton我不能对上面的答案发表评论,所以我会提交一个新答案。

这可能是我对c#语法的不熟悉,但我相信你的答案是缺少带有单位法向量的点积。 公式应该是:

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

其中n指的是平面的单位法向量, . 点到产品和x十字产品。

可以使用多边形的任意3个向量计算法线:

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

这是使用Vector.js lib的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; };