Files
ViewDesignEngine/tests/brep/test_direct_modeling.cpp
T

285 lines
10 KiB
C++
Raw Normal View History

#include <gtest/gtest.h>
#include "vde/brep/brep.h"
#include "vde/brep/modeling.h"
#include "vde/brep/direct_modeling.h"
#include "vde/brep/brep_validate.h"
#include "vde/core/transform.h"
#include <cmath>
using namespace vde::brep;
using namespace vde::core;
using namespace vde::curves;
// ═══════════════════════════════════════════════════════════
// tweak_face — 面偏移测试
// ═══════════════════════════════════════════════════════════
TEST(DirectModelingTest, TweakFace_BoxTopOffset) {
auto box = make_box(2, 2, 2);
auto result = tweak_face(box, 0, 0.5);
EXPECT_TRUE(result.success) << "tweak_face should succeed for box top face";
EXPECT_GT(result.body.num_faces(), 0u);
// After tweak, model should still be valid
auto val = validate(result.body);
EXPECT_TRUE(val.valid) << "Offset model should still be valid";
}
TEST(DirectModelingTest, TweakFace_BoxInwardOffset) {
auto box = make_box(2, 2, 2);
auto result = tweak_face(box, 0, -0.3);
EXPECT_TRUE(result.success);
auto val = validate(result.body);
EXPECT_TRUE(val.valid);
}
TEST(DirectModelingTest, TweakFace_ZeroDelta) {
auto box = make_box(2, 2, 2);
auto result = tweak_face(box, 0, 0.0);
EXPECT_TRUE(result.success);
// Zero delta should produce valid model (essentially unchanged)
auto val = validate(result.body);
EXPECT_TRUE(val.valid);
EXPECT_EQ(result.body.num_faces(), box.num_faces());
}
TEST(DirectModelingTest, TweakFace_InvalidFaceId) {
auto box = make_box(2, 2, 2);
auto result = tweak_face(box, 99, 0.5);
EXPECT_FALSE(result.success);
EXPECT_FALSE(result.errors.empty());
}
TEST(DirectModelingTest, TweakFace_CylinderCap) {
auto cyl = make_cylinder(2, 4, 32);
auto result = tweak_face(cyl, 0, 0.5);
EXPECT_TRUE(result.success);
auto val = validate(result.body);
EXPECT_TRUE(val.valid);
}
// ═══════════════════════════════════════════════════════════
// move_face — 面移动测试
// ═══════════════════════════════════════════════════════════
TEST(DirectModelingTest, MoveFace_Translate) {
auto box = make_box(2, 2, 2);
auto T = translate(Vector3D(0.5, 0, 0));
auto result = move_face(box, 0, T);
EXPECT_TRUE(result.success);
auto val = validate(result.body);
EXPECT_TRUE(val.valid);
}
TEST(DirectModelingTest, MoveFace_Rotate) {
auto box = make_box(2, 2, 2);
// Rotate around Z axis by small angle
auto T = rotate_z(M_PI / 12);
auto result = move_face(box, 0, T);
// Small rotation should succeed
EXPECT_TRUE(result.success);
auto val = validate(result.body);
EXPECT_TRUE(val.valid);
}
TEST(DirectModelingTest, MoveFace_TranslateAndRotate) {
auto box = make_box(2, 2, 2);
auto T = translate(Vector3D(0.2, 0.1, 0)) * rotate_z(0.1);
auto result = move_face(box, 0, T);
EXPECT_TRUE(result.success);
auto val = validate(result.body);
EXPECT_TRUE(val.valid);
}
TEST(DirectModelingTest, MoveFace_LargeRotationFails) {
auto box = make_box(2, 2, 2);
// Large rotation should cause normal flip and be rejected
auto T = rotate_z(2.0); // ~115 degrees
auto result = move_face(box, 0, T);
// May succeed or fail depending on detection, but should not crash
// At minimum the result body should exist
EXPECT_GE(result.body.num_faces(), 0u);
}
TEST(DirectModelingTest, MoveFace_InvalidFaceId) {
auto box = make_box(2, 2, 2);
auto T = translate(Vector3D(0.5, 0, 0));
auto result = move_face(box, 99, T);
EXPECT_FALSE(result.success);
EXPECT_FALSE(result.errors.empty());
}
// ═══════════════════════════════════════════════════════════
// replace_face — 面替换测试
// ═══════════════════════════════════════════════════════════
TEST(DirectModelingTest, ReplaceFace_WithSameSurface) {
auto box = make_box(2, 2, 2);
// Replace face 0 with its own surface (should succeed)
const auto& orig_face = box.face(0);
const auto& orig_surf = box.surface(orig_face.surface_id);
auto result = replace_face(box, 0, orig_surf);
EXPECT_TRUE(result.success);
auto val = validate(result.body);
EXPECT_TRUE(val.valid);
}
TEST(DirectModelingTest, ReplaceFace_WithCurvedSurface) {
auto box = make_box(2, 2, 2);
// Create a slightly curved surface matching the top face boundary
// Top face of box(2,2,2) has corners: (-1,-1,1), (1,-1,1), (1,1,1), (-1,1,1)
std::vector<std::vector<Point3D>> grid = {
{Point3D(-1, -1, 1.0), Point3D(-1, 1, 1.0)},
{Point3D( 1, -1, 1.2), Point3D( 1, 1, 1.2)} // curved up in middle
};
NurbsSurface curved(grid, {0,0,1,1}, {0,0,1,1}, {}, 1, 1);
auto result = replace_face(box, 0, curved);
EXPECT_TRUE(result.success);
auto val = validate(result.body);
EXPECT_TRUE(val.valid);
}
TEST(DirectModelingTest, ReplaceFace_InvalidFaceId) {
auto box = make_box(2, 2, 2);
const auto& orig_face = box.face(0);
const auto& orig_surf = box.surface(orig_face.surface_id);
auto result = replace_face(box, 99, orig_surf);
EXPECT_FALSE(result.success);
}
// ═══════════════════════════════════════════════════════════
// push_pull — 推拉测试
// ═══════════════════════════════════════════════════════════
TEST(DirectModelingTest, PushPull_ExtrudePositive) {
auto box = make_box(2, 2, 2);
auto result = push_pull(box, 0, 1.0);
EXPECT_TRUE(result.success);
// Extrusion should add side faces
EXPECT_GT(result.new_faces, 0);
EXPECT_GT(result.body.num_faces(), box.num_faces());
auto val = validate(result.body);
EXPECT_TRUE(val.valid);
}
TEST(DirectModelingTest, PushPull_CutNegative) {
auto box = make_box(2, 2, 2);
auto result = push_pull(box, 0, -0.3);
EXPECT_TRUE(result.success);
// Cut should not add new faces, but reshape existing ones
auto val = validate(result.body);
EXPECT_TRUE(val.valid);
}
TEST(DirectModelingTest, PushPull_ZeroDistance) {
auto box = make_box(2, 2, 2);
auto result = push_pull(box, 0, 0.0);
// Zero distance push-pull: model unchanged (side faces degenerate)
EXPECT_GE(result.body.num_faces(), box.num_faces());
auto val = validate(result.body);
EXPECT_TRUE(val.valid);
}
TEST(DirectModelingTest, PushPull_InvalidFaceId) {
auto box = make_box(2, 2, 2);
auto result = push_pull(box, 99, 1.0);
EXPECT_FALSE(result.success);
}
// ═══════════════════════════════════════════════════════════
// offset_face — 面等距偏移测试
// ═══════════════════════════════════════════════════════════
TEST(DirectModelingTest, OffsetFace_Basic) {
auto box = make_box(2, 2, 2);
auto result = offset_face(box, 0, 0.3);
EXPECT_TRUE(result.success);
auto val = validate(result.body);
EXPECT_TRUE(val.valid);
}
TEST(DirectModelingTest, OffsetFace_Inward) {
auto box = make_box(2, 2, 2);
auto result = offset_face(box, 0, -0.2);
EXPECT_TRUE(result.success);
auto val = validate(result.body);
EXPECT_TRUE(val.valid);
}
// ═══════════════════════════════════════════════════════════
// Validation after operations — 操作后模型验证
// ═══════════════════════════════════════════════════════════
TEST(DirectModelingTest, AllOps_PreserveValidation) {
auto box = make_box(2, 2, 2);
// tweak_face
auto r1 = tweak_face(box, 0, 0.3);
EXPECT_TRUE(validate(r1.body).valid);
// move_face
auto r2 = move_face(box, 0, translate(Vector3D(0.1, 0, 0)));
EXPECT_TRUE(validate(r2.body).valid);
// push_pull
auto r3 = push_pull(box, 0, 0.5);
EXPECT_TRUE(validate(r3.body).valid);
// offset_face
auto r4 = offset_face(box, 0, 0.2);
EXPECT_TRUE(validate(r4.body).valid);
}
// ═══════════════════════════════════════════════════════════
// 退化情况 — 非法位置测试
// ═══════════════════════════════════════════════════════════
TEST(DirectModelingTest, Degenerate_NegativeFaceId) {
auto box = make_box(2, 2, 2);
auto result = tweak_face(box, -1, 0.5);
EXPECT_FALSE(result.success);
}
TEST(DirectModelingTest, Degenerate_MoveIntoSelfIntersection) {
auto box = make_box(2, 2, 2);
// Move a face far enough to flip the normal → should be detected
auto T = rotate_x(M_PI); // 180° rotation
auto result = move_face(box, 0, T);
// Should at minimum produce a body (even if invalid)
EXPECT_GE(result.body.num_faces(), 0u);
}
TEST(DirectModelingTest, Degenerate_ExcessivePushPull) {
auto box = make_box(2, 2, 2);
// Push-pull with very large negative distance (cut through)
auto result = push_pull(box, 0, -5.0);
EXPECT_TRUE(result.success || !result.errors.empty());
// Should not crash even with excessive distance
}