Browse Source
			
			
			
			
				
		- Implement robust surface area and volume computation for polymesh_t - Support arbitrary polygonal faces (triangles, quads, etc.) via fan triangulation - Add vector math utilities: cross, dot, norm, and signed volume - Validate algorithm correctness using unit cube test case - Ensure right-hand rule compliance for accurate volume sign Test: Added test case with unit cube (8 vertices, 6 quads) Expected: area = 6.0, volume = 1.0 → actual results matchfeat-integrator
				 3 changed files with 149 additions and 0 deletions
			
			
		@ -0,0 +1,104 @@ | 
				
			|||
#include <cmath> | 
				
			|||
#include <cstdio> | 
				
			|||
#include <solve.h>  // polymesh_t | 
				
			|||
 | 
				
			|||
 | 
				
			|||
// Vector subtraction: a - b
 | 
				
			|||
inline vector3d vec3_sub(const vector3d& a, const vector3d& b) { | 
				
			|||
    return { a.x - b.x, a.y - b.y, a.z - b.z }; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
// Vector cross product: a × b
 | 
				
			|||
inline vector3d vec3_cross(const vector3d& a, const vector3d& b) { | 
				
			|||
    return { | 
				
			|||
        a.y * b.z - a.z * b.y, | 
				
			|||
        a.z * b.x - a.x * b.z, | 
				
			|||
        a.x * b.y - a.y * b.x | 
				
			|||
    }; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
// Vector dot product: a · b
 | 
				
			|||
inline double vec3_dot(const vector3d& a, const vector3d& b) { | 
				
			|||
    return a.x * b.x + a.y * b.y + a.z * b.z; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
// Vector length: ||a||
 | 
				
			|||
inline double vec3_norm(const vector3d& a) { | 
				
			|||
    return std::sqrt(a.x*a.x + a.y*a.y + a.z*a.z); | 
				
			|||
} | 
				
			|||
 | 
				
			|||
// Compute triangle area: 0.5 * || (b-a) × (c-a) ||
 | 
				
			|||
inline double triangle_area(const vector3d& a, const vector3d& b, const vector3d& c) { | 
				
			|||
    vector3d ab = vec3_sub(b, a); | 
				
			|||
    vector3d ac = vec3_sub(c, a); | 
				
			|||
    vector3d cross_product = vec3_cross(ab, ac); | 
				
			|||
    return 0.5 * vec3_norm(cross_product); | 
				
			|||
} | 
				
			|||
 | 
				
			|||
// Compute triangle signed volume contribution (w.r.t origin): (a · (b × c)) / 6
 | 
				
			|||
inline double triangle_signed_volume(const vector3d& a, const vector3d& b, const vector3d& c) { | 
				
			|||
    vector3d cross_product = vec3_cross(b, c); | 
				
			|||
    return vec3_dot(a, cross_product) / 6.0; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
// Compute polygon face area (triangulate as fan)
 | 
				
			|||
double polygon_area(const vector3d* vertices, const uint32_t* face_indices, uint32_t n) { | 
				
			|||
    if (n < 3) return 0.0; | 
				
			|||
    double area = 0.0; | 
				
			|||
    const vector3d& v0 = vertices[face_indices[0]]; | 
				
			|||
    for (uint32_t i = 1; i < n - 1; ++i) { | 
				
			|||
        const vector3d& v1 = vertices[face_indices[i]]; | 
				
			|||
        const vector3d& v2 = vertices[face_indices[i + 1]]; | 
				
			|||
        area += triangle_area(v0, v1, v2); | 
				
			|||
    } | 
				
			|||
    return area; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
// Compute polygon signed volume contribution (sum of triangle contributions)
 | 
				
			|||
double polygon_signed_volume(const vector3d* vertices, const uint32_t* face_indices, uint32_t n) { | 
				
			|||
    if (n < 3) return 0.0; | 
				
			|||
    double volume = 0.0; | 
				
			|||
    const vector3d& v0 = vertices[face_indices[0]]; | 
				
			|||
    for (uint32_t i = 1; i < n - 1; ++i) { | 
				
			|||
        const vector3d& v1 = vertices[face_indices[i]]; | 
				
			|||
        const vector3d& v2 = vertices[face_indices[i + 1]]; | 
				
			|||
        volume += triangle_signed_volume(v0, v1, v2); | 
				
			|||
    } | 
				
			|||
    return volume; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
// 🟩 Main: compute total mesh surface area
 | 
				
			|||
double compute_surface_area(const polymesh_t* mesh) { | 
				
			|||
    if (!mesh || !mesh->vertices || !mesh->faces || !mesh->vertex_counts) { | 
				
			|||
        return 0.0; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    double total_area = 0.0; | 
				
			|||
    const uint32_t* face_ptr = mesh->faces;  // current index into faces array
 | 
				
			|||
 | 
				
			|||
    for (uint32_t i = 0; i < mesh->num_faces; ++i) { | 
				
			|||
        uint32_t n = mesh->vertex_counts[i]; | 
				
			|||
        total_area += polygon_area(mesh->vertices, face_ptr, n); | 
				
			|||
        face_ptr += n;  // move to the next face's start index
 | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    return total_area; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
// 🟦 Main: compute total mesh volume (requires closed mesh with outward normals)
 | 
				
			|||
double compute_volume(const polymesh_t* mesh) { | 
				
			|||
    if (!mesh || !mesh->vertices || !mesh->faces || !mesh->vertex_counts) { | 
				
			|||
        return 0.0; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    double total_volume = 0.0; | 
				
			|||
    const uint32_t* face_ptr = mesh->faces; | 
				
			|||
 | 
				
			|||
    for (uint32_t i = 0; i < mesh->num_faces; ++i) { | 
				
			|||
        uint32_t n = mesh->vertex_counts[i]; | 
				
			|||
        total_volume += polygon_signed_volume(mesh->vertices, face_ptr, n); | 
				
			|||
        face_ptr += n; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    return std::abs(total_volume);  // ensure positive volume
 | 
				
			|||
} | 
				
			|||
@ -0,0 +1,37 @@ | 
				
			|||
#include <iostream> | 
				
			|||
#include <mesh_algorithm.hpp> | 
				
			|||
 | 
				
			|||
int main() { | 
				
			|||
    // 8 vertices: unit cube
 | 
				
			|||
    vector3d verts[8] = { | 
				
			|||
        {0,0,0}, {1,0,0}, {1,1,0}, {0,1,0}, | 
				
			|||
        {0,0,1}, {1,0,1}, {1,1,1}, {0,1,1} | 
				
			|||
    }; | 
				
			|||
 | 
				
			|||
    // 6 faces, each with 4 vertices (counter-clockwise order when viewed from outside)
 | 
				
			|||
    uint32_t face_data[] = { | 
				
			|||
        0,1,2,3,  // bottom face
 | 
				
			|||
        7,6,5,4,  // top face (note CCW when seen from outside)
 | 
				
			|||
        0,4,5,1,  // front face
 | 
				
			|||
        1,5,6,2,  // right face
 | 
				
			|||
        3,2,6,7,  // back face
 | 
				
			|||
        0,3,7,4   // left face
 | 
				
			|||
    }; | 
				
			|||
 | 
				
			|||
    uint32_t vertex_counts[6] = {4, 4, 4, 4, 4, 4}; | 
				
			|||
 | 
				
			|||
    polymesh_t mesh; | 
				
			|||
    mesh.vertices = verts; | 
				
			|||
    mesh.faces = face_data; | 
				
			|||
    mesh.vertex_counts = vertex_counts; | 
				
			|||
    mesh.num_vertices = 8; | 
				
			|||
    mesh.num_faces = 6; | 
				
			|||
 | 
				
			|||
    double area = compute_surface_area(&mesh); | 
				
			|||
    double volume = compute_volume(&mesh); | 
				
			|||
 | 
				
			|||
    std::cout << "area: " << area << std::endl;  // expected: 6
 | 
				
			|||
    std::cout << "volume: " << volume << std::endl;  // expected: 1
 | 
				
			|||
 | 
				
			|||
    return 0; | 
				
			|||
} | 
				
			|||
					Loading…
					
					
				
		Reference in new issue