我有一个3D表面,(想想xy平面)。飞机可以倾斜。 (想想一条斜坡路)。
给定定义表面的3D坐标列表(Point3D1X
,Point3D1Y
,Point3D1Z
,Point3D12X
,Point3D2Y
,Point3D2Z
, Point3D3X
,Point3D3Y
,Point3D3Z
等),如何计算表面积?
请注意,我的问题类似于在2D平面中查找区域。在2D平面中,我们有一个定义多边形的点列表,使用这个点列表我们可以找到多边形的面积。现在假设所有这些点都具有z
值,使得它们在3D中升高以形成表面。我的问题是如何找到3D表面的区域?
答案 0 :(得分:10)
既然你说它是一个多面体,堆叠器的链接( 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;
}
答案 1 :(得分:3)
我赞成a few answers,我认为这是正确的。但我认为最简单的方法 - 无论是2D还是3D,都是使用以下公式:
area = sum(V(i+1)XV(i))/2;
其中X
是vector cross。
执行此操作的代码是:
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平面,我认为这是笨重的。
您怎么看?
答案 2 :(得分:2)
您的意思是3D刨床多边形区域吗?
答案 3 :(得分:1)
您可以根据2D解决方案推导出解决方案。
从一堆较小的三角形中考虑多边形。
将每个三角形投影回XY平面。您可以显示原始三角形的面积是投影三角形面积的1 /(n.k)倍。 (这里n是与包含多边形的平面垂直的单位,k是z方向上的单位矢量)
因此原件的总面积是投影到XY平面的多边形面积的1 /(n.k)倍。您可以使用现有的2D公式计算出来。
您可以通过(e1 x e2)/ ||计算n e1 x e2 ||其中e1和e2是多边形的任何2个非平行边。
当然,你可以通过投射到XZ或YZ平面来获得更好(更准确)的结果。你应该选择一个最接近你的平面的法线。
答案 4 :(得分:1)
我不知道如何优化这种方法(我之前没有在代码中完成),但是数学方法是将形状分成三角形,然后可以很容易地计算和求和。 (请记住:三角形的面积是宽*高* 0.5 - 你需要计算非直角三角形的高度。)
在3D中执行这些操作通常意味着每个阶段都需要再进行一次计算。例如,在2D中,2点之间的距离(形状的一边的长度)计算如下(pseduocode,因为我在这台机器上没有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);
}
将形状拆分为任意三角形只需要一次挑选任意三个相邻的顶点,直到最后三个为止。
答案 5 :(得分:1)
另一种不需要您创建多边形网格的解决方案是围绕周边进行轮廓积分。您使用Green's theorem将区域积分转换为轮廓积分,然后使用像Gauss quadrature这样的简单函数来对每个贡献进行积分和求和。你必须有一个周界的定义。
此过程也适用于具有孔的2D形状。您只需要定义从外周到孔的切口,围绕孔整合,然后返回到周边。
答案 6 :(得分:1)
@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;
};