Получить площадь поверхности многогранника (3D-объект) - PullRequest
11 голосов
/ 28 февраля 2010

У меня есть трехмерная поверхность (подумайте о плоскости xy). Самолет может быть наклонным. (подумайте о склоне дороги).

Имеется список трехмерных координат, определяющих поверхность (Point3D1X, Point3D1Y, Point3D1Z, Point3D12X, Point3D2Y, Point3D2Z, Point3D3X, Point3D3Y, Point3D3Z, и тд), как рассчитать площадь поверхности?

Обратите внимание, что мой вопрос здесь аналогичен поиску области в 2D плоскости. В 2D-плоскости у нас есть список точек, который определяет многоугольник, и используя этот список точек, мы можем найти площадь многоугольника. Теперь предположим, что все эти точки имеют значения z таким образом, что они поднимаются в 3D для формирования поверхности. У меня вопрос, как найти площадь этой трехмерной поверхности?

Ответы [ 7 ]

10 голосов
/ 28 февраля 2010

Поскольку вы говорите, что это многогранник, применяется ссылка на укладчик (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;
}
3 голосов
/ 28 февраля 2010

Я проголосовал несколько ответов , которые я считаю верными. Но я думаю, что самый простой способ сделать это - независимо от того, в 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, которая, на мой взгляд, неуклюжа.

Что вы думаете?

2 голосов
/ 28 февраля 2010
1 голос
/ 23 мая 2013

@ 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))

Вот реализация javascript с использованием библиотеки Vector.js:

  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 голос
/ 28 февраля 2010

Другое решение, которое не потребует от вас создания сетки многоугольников, - это сделать контурный интеграл по периметру. Вы используете теорему Грина для преобразования интеграла площади в контурный интеграл, а затем используете нечто простое, например квадратура Гаусса , чтобы интегрировать и суммировать каждый вклад. Вы должны иметь определение периметра.

Этот процесс может работать и с 2D-фигурами, которые также имеют отверстия. Вам просто нужно определить разрез, который проходит от внешнего периметра к отверстию, объединить вокруг отверстия и затем вернуться к периметру.

1 голос
/ 28 февраля 2010

Я не знаю, как оптимизировать этот метод (я не делал этого раньше в коде), но математический подход к нему заключается в разделении фигуры на треугольники, площадь которых затем легко вычисляется и суммируется. (Помните: площадь треугольника равна ширине * высоте * 0,5 - вам нужно будет вычислить высоту прямоугольников с неправильными углами.)

Выполнение этих вещей в 3D обычно означает, что на каждом этапе требуется еще один расчет. Например, в 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);
}

В трех измерениях это становится:

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 голос
/ 28 февраля 2010

Вы можете вывести решение в терминах 2D-решения.

Представьте, что многоугольник сошел с ума от кучи меньших треугольников.

Спроецируйте каждый треугольник обратно на плоскость XY.Вы можете показать, что площадь оригинального треугольника в 1 / (nk) раз больше площади проецируемого треугольника.(Здесь n - это единица, нормальная к плоскости, содержащей многоугольник, а k - это единичный вектор в направлении z)

Таким образом, общая площадь оригинала в 1 / (nk) раз превышает площадь многоугольникапроецируется в плоскости XY.Что вы можете решить, используя существующую 2D формулу.

Вы можете рассчитать n по (e1 x e2) / ||e1 x e2 ||где e1 и e2 - любые 2 непараллельных ребра вашего многоугольника.

Конечно, вы можете получить более точные (более точные) результаты, проецируясь в плоскость XZ или YZ. Вы должны выбрать тот, у которого нормали ближе всего к плоскости вашего самолета.

...