У меня есть 3D поверхность, (думайте о xy плоскости). Плоскость может быть наклонной. (думайте о наклонной дороге).
Учитывая список 3D координат, которые определяют поверхность (Point3D1X
, Point3D1Y
, Point3D1Z
, Point3D12X
, Point3D2Y
, Point3D2Z
, Point3D3X
, Point3D3Y
, Point3D3Z
, и так далее), как вычислить область поверхности?
Обратите внимание, что мой вопрос здесь походит на область открытия в 2D плоскости. В 2D плоскости у нас есть список точек, который определяет полигон, и использующий этот список точек, мы можем найти область полигона. Теперь предполагая, что все эти точки имеют z
значения таким способом, которым они подняты в 3D для формирования поверхности. Мой вопрос состоит в том, как найти область той 3D поверхности?
Я поддержал несколько ответов , которые я считаю правильными. Но я думаю, что самый простой способ сделать это - независимо от того, в 2D он или в 3D, - это использовать следующую формулу:
area = sum(V(i+1)XV(i))/2;
Где X
- это пересечение векторов .
Код для этого:
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, что я считаю неудобным.
Что вы думаете?
Я не знаю, как оптимизировать этот метод (раньше я не делал этого в коде), но математический способ подойти к нему - разбить форму на треугольники, площадь которых затем легко вычисляется и суммируется. (Помните: площадь треугольника равна ширине * высоте * 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);
}
. Разделение фигуры на произвольные треугольники включает в себя выбор любых трех смежных вершин за раз, пока вы не дойдете до последних трех.
Другое решение, которое не потребует от вас создания сетки из многоугольников, - это выполнение контурного интеграла по периметру. Вы используете теорему Грина , чтобы преобразовать интеграл площадей в контурный интеграл, а затем используете что-то простое, например квадратур Гаусса , чтобы интегрировать и суммировать каждый вклад. У вас обязательно должно быть определение периметра.
Этот процесс может работать с 2D-формами, в которых также есть отверстия. Вам просто нужно определить разрез, который проходит от внешнего периметра к отверстию, объединяется вокруг отверстия и затем возвращается к периметру.
Вы можете получить решение в терминах 2D-решения.
Рассмотрим многоугольник, образованный грудой меньших треугольников.
Спроецируйте каждый треугольник обратно в плоскость XY. Вы можете показать, что площадь исходного треугольника в 1 / (n.k) умножена на площадь проецируемого треугольника. (Здесь n - единичная нормаль к плоскости, содержащей многоугольник, а k - единичный вектор в направлении z)
Таким образом, общая площадь оригинала равна 1 / (nk), умноженному на площадь многоугольника, спроецированного в Плоскость XY.Что вы можете решить, используя существующую 2D-формулу.
Вы можете вычислить n как (e1 x e2) / || e1 x e2 || где e1 и e2 - любые 2 непараллельных ребра многоугольника.
Конечно, вы можете получить лучшие (более точные) результаты, проецируя на плоскость XZ или YZ .. вы должны выбрать тот, нормаль которого наиболее близка к вашей плоскости.
Поскольку вы говорите, что это многогранник, ссылка на накопитель ( 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;
}