Calculate volume of convex solid?

Started by
5 comments, last by _WeirdCat_ 8 years, 10 months ago

Given set of points that define faces of a convex solid is it possible to calculate its volume without any fancy stuff? (object can be defined aswell as set of planes that close the shape)

Advertisement

Depends on what you consider as fancy, but yes, it is possible to derive a formula for this. See Graphics Gems 2, p.170: "IV.1. Area of Planar Polygons and Volume of Polyhedra." for a ready formula. Also MathGeoLib has code for this: http://clb.demon.fi/MathGeoLib/nightly/docs/Polyhedron_Volume.php

If you're doing 2D stuff you can find an example here (ctrl + f for the term: area) http://box2d.googlecode.com/svn/trunk/Box2D/Box2D/Collision/Shapes/b2PolygonShape.cpp.

If in 3D you can looking at that Graphics Gems article clb linked.

mhm that looks way too simpe i cant believe that would work.


 /** The implementation of this function is based on Graphics Gems 2, p. 170: "IV.1. Area of Planar Polygons and Volume of Polyhedra." */
 float Polyhedron::Volume() const
{
      float volume = 0.f;
        for(int i = 0; i < NumFaces(); ++i)
       {
                Polygon face = FacePolygon(i); ///@todo Optimize temporary copies.
                volume += face.Vertex(0).Dot(face.NormalCCW()) * face.Area();
       }
       return Abs(volume) / 3.f;
 }

i can't find for now any copy of this book but i have two questions first is

can i use NormalCW instead of NormalCCW (is this really a thing)

second thing why abs(volume) / 3 ? :P

Don't know the exact mathematical proof, but you should definitely be able to use CW or CCW, because of the Abs.

dot(p, n) gives exactly the 'd' in the plane equation for the polygon, ax + by + cz - d = 0.

So the algorithm works by multiplying the distance to the polygon from the origin by its area. To visualize, think of for example a sphere at the origin where this pretty obviously works, and if you move it away from the origin the distance to the polygons in the one direction will be increased and therefore their volume-contribution will increase, and be corrected by the 'd' for the polygons in the opposite direction being smaller or having changed sign if moved past the origin.

dot(p, n) will be negative or positive depending on whether the polygon faces the origin or away from it.

As long as the volume is closed without gaps or overlapping polygons and every polygon faces the same direction (in or out) it will always work, as any larger polygon will require either another large polygon or many smaller ones with opposite normal to close the volume.

The volume algorithm works by taking each face. Then we work in a space where the face is axis aligned. The dot product in this space gets the distance from the face plane to the origin (doesnt matter which vertex is used - theyre all on the same plane!).

The edge/area of the polygon is proportional to this origin-to-plane distance - its 0 at origin, full value at the plane end.

For 2D we integrate length of edge over this distance to get area. For 3D we integrate face area over the distance to get volume.

2D:

Integrate(0->1) edge*x dx (edge(x) = edge*x)

=> edge*x*x/2 (x = 1 for full area)

=> edge/2 (so in 2D youd divide by 2 instead of 3)

3D:

Integrate(0->1) area*x*x dx (area(x) = area*x*x because area grows/shrinks on 2 dimensions as x changes instead of just 1)

=> area*x*x*x/3 (x = 1 for full volume)

=> area/3 (so we divide by 3 in 3D)

I guess you can generalize it so it always divides by the amount of dimensions...

o3o

not sure if it works right but for 1x1x1 cube it gave me 1 so i assume its a good code



void CalculateFaceAreas()
{
 //call calc face center point
	FACE_AREA = new T[ FaceLength];
    for(int i = 0; i < FaceLength; ++i)
	{
		T area = T(0.0);
		
int ix 	= VBO_BE[ i ].INDEX_START;
int x 	= VBO_BE[ i ].INDEX_START + VBO_BE[ i ].length;
					   int len = VBO_BE[ i ].length;
					   int index1 	= ix;
for (int n = 0; n < len; n++)
	{
int index2 	= ix + n;
int index3 	= ix + n + 1;

if (n == len - 1) index3 = ix;

T a = n3ddistance( FACE_CENTER_POINT[i], AOS[ index2 ].v );
T b = n3ddistance( FACE_CENTER_POINT[i], AOS[ index3 ].v );
T c = n3ddistance( AOS[ index2 ].v, AOS[ index3 ].v );

T p = (a+b+c) / 2.0;

T r = sqrt(p*(p-a)*(p-b)*(p-c));

area = area + r;
if (n == len - 1) break;

	}

    	FACE_AREA[ i ] = T( int(area*10000.0) / 10000.0 );
    }

}

T CalculateVolume()
{
	//remember to call CalculateFaceAreas() before
     T volume = 0.f;
       for(int i = 0; i < FaceLength; ++i)
      {
int index = VBO_BE[ i ].INDEX_START;

float da = Dot( AOS[ index ].v, FACE_N[ i ] ) * FACE_AREA[ i ];

			   volume = volume + da;
      }
      return absnf(volume) / T(3.0);
}

This topic is closed to new replies.

Advertisement