73df04d5cb
M2.1 — G2/G3 连续性分析 (Agent #0): - surface_continuity.h/.cpp: G0/G1/G2/G3 curve/surface detection - Weingarten equation for curvature, Frénet frame, zebra stripe - surface_analysis.h/.cpp: curvature_map, deviation_analysis, curvature_comb - 24 tests (12 continuity + 12 analysis) M2.2 — 曲面延伸 + N边填充 (Agent #1): - surface_extension.h/.cpp: extend_surface(G1/G2), n_sided_fill, blend_surfaces - Coons patch generalization for N-sided holes - 16/16 tests passed in Docker container M2.3 — 高级过渡曲面 (Agent #2): - advanced_blend.h/.cpp: real implementations replacing stubs - variable_radius_blend, multi_face_blend, rolling_ball_blend, face_face_blend - Ball-rolling envelope + corner sphere filling - 15+ tests with validate() verification
159 lines
6.1 KiB
C++
159 lines
6.1 KiB
C++
#include <gtest/gtest.h>
|
|
#include "vde/brep/advanced_blend.h"
|
|
#include "vde/brep/modeling.h"
|
|
#include "vde/brep/brep_validate.h"
|
|
#include "vde/brep/brep.h"
|
|
#include <cmath>
|
|
|
|
using namespace vde::brep;
|
|
using namespace vde::core;
|
|
|
|
// ═══════════════════════════════════════════════
|
|
// variable_radius_blend 测试 (4项)
|
|
// ═══════════════════════════════════════════════
|
|
|
|
TEST(VariableRadiusBlendTest, BoxEdgeGrowRadius) {
|
|
auto box = make_box(4, 4, 4);
|
|
auto result = variable_radius_blend(box, 0, 0.3, 0.8, 16);
|
|
// Blend adds blend-strip faces → more faces than original
|
|
EXPECT_GT(result.num_faces(), box.num_faces());
|
|
EXPECT_GT(result.num_edges(), box.num_edges());
|
|
EXPECT_GT(result.num_vertices(), 0u);
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|
|
|
|
TEST(VariableRadiusBlendTest, BoxEdgeShrinkRadius) {
|
|
auto box = make_box(4, 4, 4);
|
|
auto result = variable_radius_blend(box, 0, 0.8, 0.3, 12);
|
|
EXPECT_GT(result.num_faces(), box.num_faces());
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|
|
|
|
TEST(VariableRadiusBlendTest, BoxEdgeSymmetric) {
|
|
auto box = make_box(4, 4, 4);
|
|
// Equal start and end radius → constant-radius blend
|
|
auto r1 = variable_radius_blend(box, 0, 0.5, 0.5, 8);
|
|
auto r2 = variable_radius_blend(box, 0, 0.5, 0.5, 16);
|
|
EXPECT_GT(r1.num_faces(), box.num_faces());
|
|
// More samples = more blend strips = more faces
|
|
EXPECT_GT(r2.num_faces(), r1.num_faces());
|
|
EXPECT_TRUE(r1.is_valid());
|
|
EXPECT_TRUE(r2.is_valid());
|
|
}
|
|
|
|
TEST(VariableRadiusBlendTest, SmallRadiusNearZero) {
|
|
auto box = make_box(4, 4, 4);
|
|
auto result = variable_radius_blend(box, 1, 0.05, 0.5, 8);
|
|
EXPECT_GT(result.num_faces(), box.num_faces());
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════
|
|
// multi_face_blend 测试 (3项)
|
|
// ═══════════════════════════════════════════════
|
|
|
|
TEST(MultiFaceBlendTest, TwoFaces) {
|
|
auto box = make_box(4, 4, 4);
|
|
auto result = multi_face_blend(box, {0, 1}, 0.5);
|
|
EXPECT_GT(result.num_vertices(), 0u);
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|
|
|
|
TEST(MultiFaceBlendTest, ThreeFacesCorner) {
|
|
// Blend 3 faces meeting at a corner — spherical corner fill added
|
|
auto box = make_box(4, 4, 4);
|
|
auto result = multi_face_blend(box, {0, 2, 4}, 0.4);
|
|
EXPECT_GT(result.num_vertices(), 0u);
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|
|
|
|
TEST(MultiFaceBlendTest, AllFaces) {
|
|
auto box = make_box(4, 4, 4);
|
|
std::vector<int> all_faces;
|
|
for (size_t i = 0; i < box.num_faces(); ++i)
|
|
all_faces.push_back(static_cast<int>(i));
|
|
auto result = multi_face_blend(box, all_faces, 0.3);
|
|
EXPECT_GT(result.num_vertices(), 0u);
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════
|
|
// rolling_ball_blend 测试 (4项)
|
|
// ═══════════════════════════════════════════════
|
|
|
|
TEST(RollingBallBlendTest, StraightEdge) {
|
|
auto box = make_box(4, 4, 4);
|
|
auto result = rolling_ball_blend(box, 0, 0.5, 20);
|
|
EXPECT_GT(result.num_faces(), box.num_faces());
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|
|
|
|
TEST(RollingBallBlendTest, ConvexCorner) {
|
|
// Box edge is a convex corner
|
|
auto box = make_box(4, 4, 4);
|
|
auto result = rolling_ball_blend(box, 3, 0.6, 16);
|
|
EXPECT_GT(result.num_faces(), box.num_faces());
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|
|
|
|
TEST(RollingBallBlendTest, DifferentEdges) {
|
|
auto box = make_box(4, 4, 4);
|
|
// Test rolling ball on first 4 edges
|
|
for (int ei = 0; ei < 4; ++ei) {
|
|
auto result = rolling_ball_blend(box, ei, 0.3, 10);
|
|
EXPECT_GT(result.num_faces(), box.num_faces()) << "edge " << ei;
|
|
EXPECT_TRUE(result.is_valid()) << "edge " << ei;
|
|
}
|
|
}
|
|
|
|
TEST(RollingBallBlendTest, VaryingSamples) {
|
|
auto box = make_box(4, 4, 4);
|
|
auto r1 = rolling_ball_blend(box, 0, 0.5, 8);
|
|
auto r2 = rolling_ball_blend(box, 0, 0.5, 20);
|
|
// More samples => more blend strip faces
|
|
EXPECT_GT(r2.num_faces(), r1.num_faces());
|
|
EXPECT_TRUE(r1.is_valid());
|
|
EXPECT_TRUE(r2.is_valid());
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════
|
|
// face_face_blend 测试 (4项)
|
|
// ═══════════════════════════════════════════════
|
|
|
|
TEST(FaceFaceBlendTest, AdjacentFaces) {
|
|
auto box = make_box(4, 4, 4);
|
|
// face 0 (-Z) and face 2 (-Y) share an edge
|
|
auto result = face_face_blend(box, 0, 2, 0.5, 16);
|
|
EXPECT_GT(result.num_faces(), box.num_faces());
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|
|
|
|
TEST(FaceFaceBlendTest, DifferentAdjacentPair) {
|
|
auto box = make_box(4, 4, 4);
|
|
auto result = face_face_blend(box, 1, 4, 0.4, 12);
|
|
EXPECT_GT(result.num_faces(), box.num_faces());
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|
|
|
|
TEST(FaceFaceBlendTest, MultiplePairs) {
|
|
auto box = make_box(4, 4, 4);
|
|
auto r1 = face_face_blend(box, 0, 2, 0.3, 8);
|
|
auto r2 = face_face_blend(box, 1, 4, 0.4, 10);
|
|
auto r3 = face_face_blend(box, 3, 5, 0.35, 12);
|
|
EXPECT_GT(r1.num_faces(), box.num_faces());
|
|
EXPECT_GT(r2.num_faces(), box.num_faces());
|
|
EXPECT_GT(r3.num_faces(), box.num_faces());
|
|
EXPECT_TRUE(r1.is_valid());
|
|
EXPECT_TRUE(r2.is_valid());
|
|
EXPECT_TRUE(r3.is_valid());
|
|
}
|
|
|
|
TEST(FaceFaceBlendTest, NonSharedEdgeFallback) {
|
|
// Opposite faces — no shared edge, falls back gracefully
|
|
auto box = make_box(4, 4, 4);
|
|
auto result = face_face_blend(box, 0, 1, 0.2, 8);
|
|
// Should return a valid model (may be original if no common edge)
|
|
EXPECT_GT(result.num_faces(), 0u);
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|