7

I have a closed convex polyhedron which is defined by an array of convex polygons (faces) which are defined by arrays of vertices in 3D space. I'm trying to find the centroid of the polyhedron, assuming uniform density. At the moment I calculate it with the algorithm in this pseudo-code.

public Vector3 getCentroid() {
    Vector3 centroid = (0, 0, 0);
    for (face in faces) {
        Vector3 point = face.centroid;
        point.multiply(face.area());
        centroid.add(point);
    }
    centroid.divide(faces.size());
    return centroid;
}

This essentially takes the weighted average of the centroids of the faces. I'm not 100% sure this is correct as I haven't been able to find a correct algorithm online. If someone could either confirm my algorithm or refer me to a correct one I would appreciate it.

Thanks.


[EDIT]

So here is the actual Java code I am using to find the centroid. It breaks the polyhedron into pyramids converging on an arbitrary point inside the polyhedron. The weighted average for the pyramid centroids is based on the following formula.

Call = SUMall pyramids(Cpyramid * volumepyramid) / volumeall

Here is the (heavily commented code):

    // Compute the average of the facial centroids.
    // This gives an arbitrary point inside the polyhedron.
    Vector3 avgPoint = new Vector3(0, 0, 0);
    for (int i = 0; i < faces.size(); i++) {
        avgPoint.add(faces.get(i).centroid);
    }
    avgPoint.divide(faces.size());

    // Initialise the centroid and the volume.
    centroid = new Vector3(0, 0, 0);
    volume = 0;

    // Loop through each face.
    for (int i = 0; i < faces.size(); i++) {
        Face face = faces.get(i);

        // Find a vector from avgPoint to the centroid of the face.
        Vector3 avgToCentroid = face.centroid.clone();
        avgToCentroid.sub(avgPoint);

        // Gives the unsigned minimum distance between the face and a parallel plane on avgPoint.
        float distance = avgToCentroid.scalarProjection(face.getNormal());

        // Finds the volume of the pyramid using V = 1/3 * B * h
        // where:   B = area of the pyramid base.
        //          h = pyramid height.
        float pyramidVolume = face.getArea() * distance / 3;

        // Centroid of a pyramid is 1/4 of the height up from the base.
        // Using 3/4 here because vector is travelling 'down' the pyramid.
        avgToCentroid.multiply(0.75f);
        avgToCentroid.add(avgPoint);
        // avgToCentroid is now the centroid of the pyramid.

        // Weight it by the volume of the pyramid.
        avgToCentroid.multiply(pyramidVolume);

        volume += pyramidVolume;
    }

    // Average the weighted sum of pyramid centroids.
    centroid.divide(volume);

Please feel free to ask me any questions you may have about it or point out any errors you see.

null0pointer
  • 1,513
  • 3
  • 15
  • 29
  • I can't vouch for it but http://www.cs.berkeley.edu/~jfc/mirtich/massProps.html might be worth a look. – dmuir Feb 17 '12 at 09:26
  • 1
    The bit after "`[Edit]`" of [this answer](http://stackoverflow.com/a/4824248/71059) to a similar question looks good. – AakashM Feb 17 '12 at 09:29
  • In your code, you have initialized a centroid but never used it inside the loop. According to your formula, you divide it by the sum of all volumes at the end. Shouldn't centroid sum up all avgToCentroid's (centroid.add(avgToCentroid))? same as volume is the sum of all pyramid volumes? – susis strolch Jul 08 '13 at 16:31

2 Answers2

8

Generally that depends on the structure of your polyhedron. There are 4 possible cases:

  • Only vertices have weight, i.e. your polyhedron is the system of points. Then you can just calculate the mean value of the weighted sum of all the points:

    r_c = sum(r_i * m_i) / sum(m_i)
    

    Here r_i is the vector representing the i-th vertex, m_i - its mass. Case of equal masses leaves us with the simpler formula:

    r_c = sum(r_i) / n
    

    Where n is the number of vertices. Note that both sums are vectorized.

  • Only edges have weight, and polyhedron is essentially a carcass. This case can be reduced to the previous one by substituting each edge with vertex situated right in the middle of the edge and have the weight of the whole edge.

  • Only faces have weight. This case can be reduced to the first one as well. Each face is a 2D convex figure, of which the centroid can be found. Substituting each face with its centroid brings this case to the first one.

  • Solid polyhedron (your case, inferring from the "assuming uniform density"). This problem requires a more complicated approach. First step is to split polyhedron into tetrahedrons. Here is the short description on how to do this. For a tetrahedron centroid is situated in the point where all its medians intersect. (Median of a tetrahedron is the line that connects its vertex and the centroid of the opposite face.) The next step is to substitute each tetrahedron in the partition with its centroid. And the last step is to find the centroid of the resulting set of weighted points, which is exactly the first case.

Community
  • 1
  • 1
Andrei
  • 53,252
  • 9
  • 82
  • 104
  • Fairly sure it's the last case, given the "assuming uniform density" text in the q. – AakashM Feb 17 '12 at 09:35
  • @AakashM, your are right, just wanted to make the answer more complete. Slightly updated it though to reflect your remark. – Andrei Feb 17 '12 at 10:08
  • Yes, the case I have is a solid polyhedron. I probably should have clarified that. But yes the approach I'm going to take is similar to your fourth point but not exactly the same. I'm Going to split the polyhedron into several pyramids as described in AakashM's comment on my question. I had actually thought of this approach before but thought I could use the face centroids and face areas instead and not have to do as much calculation. But anyway, after I do that the problem turns into exactly your first case. Thanks for your help. – null0pointer Feb 17 '12 at 10:42
2

For the solid case, there's a much simpler method than trying to tetrahedralize the polyhedron (which has pitfalls).

Here's pseudo-ish java-ish code (assuming decent Vector3 implementation):

// running sum for total volume
double vol = 0;
// running sum for centroid
Vector3 centroid = (0, 0, 0);
for each triangle (a,b,c)
{
  // Compute area-magnitude normal
  Vector3 n = (b-a).cross(c-a);
  vol += a.dot(n)/6.;
  // Compute contribution to centroid integral for each dimension
  for(int d = 0;d<3;d++)
    centroid[d] += n[d] * ((a[d]+b[d])^2 + (b[d]+c[d])^2 + (c[d]+a[d])^2);
}
// final scale by inverse volume
centroid *= 1./(24.*2.*vol);

Note, if you have higher degree faces than triangles you can trivially triangulate with a fan and this will still work.

This conveniently works even if the polyhedron is not convex.

I've also posted matlab code

Alec Jacobson
  • 5,326
  • 4
  • 41
  • 75