2026-07-23 12:36:39 +00:00
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
#include "vde/mesh/mesh_quality.h"
|
|
|
|
|
#include "vde/mesh/halfedge_mesh.h"
|
|
|
|
|
|
|
|
|
|
using namespace vde::mesh;
|
|
|
|
|
|
|
|
|
|
TEST(MeshQualityTest, EquilateralTriangle_PerfectQuality) {
|
|
|
|
|
HalfedgeMesh mesh;
|
|
|
|
|
// Equilateral triangle side length ≈ 1.155 for area ≈ 0.577
|
|
|
|
|
double h = std::sqrt(3.0) / 2.0;
|
|
|
|
|
mesh.build_from_triangles(
|
|
|
|
|
{Point3D(0, 0, 0), Point3D(1, 0, 0), Point3D(0.5, h, 0)},
|
|
|
|
|
{{0, 1, 2}}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
auto q = evaluate_mesh_quality(mesh);
|
|
|
|
|
|
|
|
|
|
// Equilateral triangle: all angles = 60°
|
|
|
|
|
EXPECT_NEAR(q.min_angle_deg, 60.0, 1.0);
|
|
|
|
|
EXPECT_NEAR(q.max_angle_deg, 60.0, 1.0);
|
|
|
|
|
// Aspect ratio ~1 for equilateral
|
|
|
|
|
EXPECT_NEAR(q.avg_aspect_ratio, 1.0, 0.1);
|
|
|
|
|
EXPECT_EQ(q.degenerate_faces, 0u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(MeshQualityTest, DegenerateTriangle_ZeroArea) {
|
|
|
|
|
HalfedgeMesh mesh;
|
|
|
|
|
// Collinear points → zero area
|
|
|
|
|
mesh.build_from_triangles(
|
|
|
|
|
{Point3D(0, 0, 0), Point3D(1, 0, 0), Point3D(2, 0, 0)},
|
|
|
|
|
{{0, 1, 2}}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
auto q = evaluate_mesh_quality(mesh);
|
|
|
|
|
EXPECT_EQ(q.degenerate_faces, 1u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(MeshQualityTest, ObtuseTriangle_LargeMaxAngle) {
|
|
|
|
|
HalfedgeMesh mesh;
|
|
|
|
|
// Very flat obtuse triangle: (0,0), (10,0), (0,0.1)
|
|
|
|
|
mesh.build_from_triangles(
|
|
|
|
|
{Point3D(0, 0, 0), Point3D(10, 0, 0), Point3D(0, 0.1, 0)},
|
|
|
|
|
{{0, 1, 2}}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
auto q = evaluate_mesh_quality(mesh);
|
|
|
|
|
// Max angle should be very close to 180 (or at least > 90)
|
|
|
|
|
EXPECT_GT(q.max_angle_deg, 90.0);
|
|
|
|
|
// Min angle should be very small
|
|
|
|
|
EXPECT_LT(q.min_angle_deg, 10.0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(MeshQualityTest, RightTriangle) {
|
|
|
|
|
HalfedgeMesh mesh;
|
|
|
|
|
// 3-4-5 right triangle
|
|
|
|
|
mesh.build_from_triangles(
|
|
|
|
|
{Point3D(0, 0, 0), Point3D(3, 0, 0), Point3D(0, 4, 0)},
|
|
|
|
|
{{0, 1, 2}}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
auto q = evaluate_mesh_quality(mesh);
|
|
|
|
|
// Right triangle: one angle ~90°, others ~36.87° and ~53.13°
|
|
|
|
|
EXPECT_NEAR(q.max_angle_deg, 90.0, 2.0);
|
|
|
|
|
EXPECT_GT(q.min_angle_deg, 30.0);
|
|
|
|
|
EXPECT_EQ(q.degenerate_faces, 0u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(MeshQualityTest, MultipleTriangles_Mixed) {
|
|
|
|
|
HalfedgeMesh mesh;
|
|
|
|
|
// Two triangles: one good, one degenerate
|
|
|
|
|
double h = std::sqrt(3.0) / 2.0;
|
|
|
|
|
mesh.build_from_triangles(
|
|
|
|
|
{
|
|
|
|
|
Point3D(0, 0, 0), Point3D(1, 0, 0), Point3D(0.5, h, 0), // equilateral
|
|
|
|
|
Point3D(10, 0, 0), Point3D(11, 0, 0), Point3D(12, 0, 0), // degenerate
|
|
|
|
|
},
|
|
|
|
|
{{0, 1, 2}, {3, 4, 5}}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
auto q = evaluate_mesh_quality(mesh);
|
|
|
|
|
EXPECT_EQ(q.degenerate_faces, 1u);
|
|
|
|
|
// Average aspect ratio should reflect the mix
|
2026-07-23 13:45:33 +00:00
|
|
|
EXPECT_GT(q.avg_aspect_ratio, 0.5); // mixed quality, should be > 0.5
|
2026-07-23 12:36:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(MeshQualityTest, EmptyMesh) {
|
|
|
|
|
HalfedgeMesh mesh;
|
|
|
|
|
auto q = evaluate_mesh_quality(mesh);
|
|
|
|
|
EXPECT_EQ(q.degenerate_faces, 0u);
|
|
|
|
|
// Default values for empty mesh
|
|
|
|
|
EXPECT_DOUBLE_EQ(q.min_angle_deg, 0.0);
|
|
|
|
|
EXPECT_DOUBLE_EQ(q.max_angle_deg, 0.0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(MeshQualityTest, HighAspectRatioTriangle) {
|
|
|
|
|
HalfedgeMesh mesh;
|
|
|
|
|
// Very thin triangle
|
|
|
|
|
mesh.build_from_triangles(
|
|
|
|
|
{Point3D(0, 0, 0), Point3D(100, 0, 0), Point3D(0, 0.01, 0)},
|
|
|
|
|
{{0, 1, 2}}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
auto q = evaluate_mesh_quality(mesh);
|
|
|
|
|
// Aspect ratio should be very high
|
|
|
|
|
EXPECT_GT(q.max_aspect_ratio, 10.0);
|
|
|
|
|
}
|