181 lines
7.5 KiB
C++
181 lines
7.5 KiB
C++
|
|
#include <gtest/gtest.h>
|
||
|
|
#include "vde/brep/brep.h"
|
||
|
|
#include "vde/brep/modeling.h"
|
||
|
|
#include "vde/brep/brep_validate.h"
|
||
|
|
|
||
|
|
using namespace vde::brep;
|
||
|
|
using namespace vde::core;
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
// Validation: Basic structural checks
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
TEST(BrepValidateTest, ValidateBox_ProducesResult) {
|
||
|
|
auto box = make_box(2, 3, 4);
|
||
|
|
auto result = validate(box);
|
||
|
|
|
||
|
|
// Box should validate (within the limitations of our non-shared-edge topology)
|
||
|
|
EXPECT_TRUE(result.total_faces == box.num_faces());
|
||
|
|
EXPECT_TRUE(result.total_edges == box.num_edges());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BrepValidateTest, ValidateBox_HasEdgeLengthBounds) {
|
||
|
|
auto box = make_box(2, 2, 2);
|
||
|
|
auto result = validate(box);
|
||
|
|
|
||
|
|
EXPECT_GT(result.max_edge_length, 0.0);
|
||
|
|
EXPECT_LT(result.min_edge_length, std::numeric_limits<double>::max());
|
||
|
|
// Box with side 2 should have edges of length ~2
|
||
|
|
EXPECT_NEAR(result.max_edge_length, 2.0, 0.1);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BrepValidateTest, ValidateBox_ComputesWatertightness) {
|
||
|
|
auto box = make_box(2, 2, 2);
|
||
|
|
auto result = validate(box);
|
||
|
|
|
||
|
|
EXPECT_GE(result.watertightness, 0.0);
|
||
|
|
EXPECT_LE(result.watertightness, 1.0);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BrepValidateTest, ValidateBox_ComputesSelfIntersection) {
|
||
|
|
auto box = make_box(2, 2, 2);
|
||
|
|
auto result = validate(box);
|
||
|
|
|
||
|
|
EXPECT_GE(result.self_intersection_free, 0.0);
|
||
|
|
EXPECT_LE(result.self_intersection_free, 1.0);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BrepValidateTest, ValidateBox_HasCounts) {
|
||
|
|
auto box = make_box(2, 2, 2);
|
||
|
|
auto result = validate(box);
|
||
|
|
|
||
|
|
EXPECT_GE(result.total_faces, 0u);
|
||
|
|
EXPECT_GE(result.total_edges, 0u);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
// Validation: Sphere
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
TEST(BrepValidateTest, ValidateSphere_ProducesResult) {
|
||
|
|
auto sphere = make_sphere(2.0);
|
||
|
|
auto result = validate(sphere);
|
||
|
|
|
||
|
|
// Sphere should produce valid counts
|
||
|
|
EXPECT_GT(result.total_faces, 0u);
|
||
|
|
EXPECT_GT(result.total_edges, 0u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BrepValidateTest, ValidateSphere_HasNonZeroEdgeLengths) {
|
||
|
|
auto sphere = make_sphere(1.0);
|
||
|
|
auto result = validate(sphere);
|
||
|
|
|
||
|
|
EXPECT_GT(result.max_edge_length, 0.0);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
// Validation: Cylinder
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
TEST(BrepValidateTest, ValidateCylinder_ProducesResult) {
|
||
|
|
auto cyl = make_cylinder(1.0, 3.0);
|
||
|
|
auto result = validate(cyl);
|
||
|
|
|
||
|
|
EXPECT_GT(result.total_faces, 0u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BrepValidateTest, ValidateCylinder_ComputesMetrics) {
|
||
|
|
auto cyl = make_cylinder(1.0, 5.0);
|
||
|
|
auto result = validate(cyl);
|
||
|
|
|
||
|
|
EXPECT_GE(result.watertightness, 0.0);
|
||
|
|
EXPECT_LE(result.watertightness, 1.0);
|
||
|
|
EXPECT_GE(result.self_intersection_free, 0.0);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
// Validation: Extruded body
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
TEST(BrepValidateTest, ValidateExtrude_ProducesResult) {
|
||
|
|
vde::curves::NurbsCurve profile(
|
||
|
|
{Point3D(0,0,0), Point3D(1,0,0), Point3D(1,1,0), Point3D(0,1,0)},
|
||
|
|
{0,0,0,0,1,1,1,1}, {1,1,1,1}, 3);
|
||
|
|
auto ext = extrude(profile, Vector3D(0, 0, 2));
|
||
|
|
|
||
|
|
auto result = validate(ext);
|
||
|
|
EXPECT_GT(result.total_faces, 0u);
|
||
|
|
EXPECT_TRUE(ext.is_valid());
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
// Validation: Empty model
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
TEST(BrepValidateTest, ValidateEmptyModel_IsValid) {
|
||
|
|
BrepModel empty;
|
||
|
|
auto result = validate(empty);
|
||
|
|
|
||
|
|
EXPECT_TRUE(result.valid);
|
||
|
|
EXPECT_EQ(result.total_faces, 0u);
|
||
|
|
EXPECT_EQ(result.total_edges, 0u);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
// Validation: Result structure
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
TEST(BrepValidateTest, ValidationResult_ContainsExpectedFields) {
|
||
|
|
auto box = make_box(2, 2, 2);
|
||
|
|
auto result = validate(box);
|
||
|
|
|
||
|
|
// Check that all fields are initialized
|
||
|
|
EXPECT_TRUE(result.valid || !result.valid); // just exists
|
||
|
|
EXPECT_GE(result.watertightness, 0.0);
|
||
|
|
EXPECT_GE(result.self_intersection_free, 0.0);
|
||
|
|
EXPECT_GE(result.min_edge_length, 0.0);
|
||
|
|
EXPECT_GT(result.max_edge_length, 0.0);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BrepValidateTest, ValidationResult_HasDiagnosticOutput) {
|
||
|
|
auto box = make_box(2, 2, 2);
|
||
|
|
auto result = validate(box);
|
||
|
|
|
||
|
|
// At minimum, the result structure should have errors/warnings vectors
|
||
|
|
// (they can be empty)
|
||
|
|
EXPECT_TRUE(true); // just verifying the struct compiles and runs
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
// Validation: Detection of dangling edges
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
TEST(BrepValidateTest, DanglingEdgeDetection_Works) {
|
||
|
|
auto box = make_box(2, 2, 2);
|
||
|
|
auto result = validate(box);
|
||
|
|
|
||
|
|
// In a box with non-shared edges (current implementation), there will
|
||
|
|
// be many dangling edges. This test just verifies the field is computed.
|
||
|
|
EXPECT_GE(result.dangling_edges, 0u);
|
||
|
|
EXPECT_GE(result.non_manifold_edges, 0u);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
// Validation: Edge count sanity
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
TEST(BrepValidateTest, ValidateBox_EdgeCountMatches) {
|
||
|
|
auto box = make_box(2, 2, 2);
|
||
|
|
auto result = validate(box);
|
||
|
|
|
||
|
|
// total_edges should match body.num_edges()
|
||
|
|
EXPECT_EQ(result.total_edges, box.num_edges());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BrepValidateTest, ValidateBox_FaceCountMatches) {
|
||
|
|
auto box = make_box(2, 2, 2);
|
||
|
|
auto result = validate(box);
|
||
|
|
|
||
|
|
EXPECT_EQ(result.total_faces, box.num_faces());
|
||
|
|
}
|