Files
ViewDesignEngine/tests/curves/test_class_a_surfacing.cpp
T
茂之钳 921c29cb22
CI / Build & Test (push) Failing after 33s
CI / Release Build (push) Failing after 29s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled
feat(v7): B-Rep deep attack + Class-A surfacing + CAM deep + performance tuning
v7.1 — B-Rep 深度攻坚 (对标 Parasolid 95%):
- advanced_healing: auto_heal_pipeline, face_splitting/merging, topology_optimization
- watertight_verification, tolerance_analysis, tolerance diagnostic report
- sheet_metal: unfold_sheet_metal (K-Factor/BFS), bend_deduction_table, create_flange
- direct_modeling enhanced: draft_face_advanced (hinge), scale_body (non-uniform), mirror_body
- 32 tests (17 healing + 15 sheet metal), syntax-check passed

v7.2 — Class-A 曲面攻坚 (对标 CGM 95%):
- class_a_surfacing: g3_blend (4-row CP), curvature_matching (Levenberg-Marquardt)
- highlight_lines, reflection_lines, iso_photes, surface_diagnosis, shape_modification
- advanced_intersection: robust_ssi (3-stage: AABB+subdivision→Newton 1e-12→singularity)
- curve_surface_intersection, self_intersection_detection (BVH)
- 30 tests (18 class-A + 12 intersection), zero compile errors

v7.3 — CAM 深化 + 性能优化:
- cam_advanced: adaptive_clearing, trochoidal_milling, rest_machining, pencil_tracing
- tool_holder_collision_check, toolpath_optimization, feed_rate_optimization
- performance_tuning: parallel_task_graph (DAG+Kahn), work_stealing_scheduler
- memory_pool_integration, cache_optimization_hints, profile_guided_layout
- Fixed BrepModel API compatibility (body.bounds()/to_mesh() instead of .faces())
- 20 tests

12 files, ~5200 lines, 82 tests
2026-07-26 22:54:46 +08:00

354 lines
11 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <gtest/gtest.h>
#include "vde/curves/class_a_surfacing.h"
#include "vde/curves/nurbs_curve.h"
#include "vde/curves/nurbs_surface.h"
#include <cmath>
using namespace vde::curves;
using namespace vde::core;
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/// 创建平面 NURBS 曲面(XY 平面,[0,1]×[0,1]
static NurbsSurface plane_surface() {
std::vector<std::vector<Point3D>> grid = {
{Point3D(0,0,0), Point3D(0,1,0)},
{Point3D(1,0,0), Point3D(1,1,0)}
};
return NurbsSurface(grid, {0,0,1,1}, {0,0,1,1}, {{1,1},{1,1}}, 1, 1);
}
/// 创建二次曲面(抛物面样)
static NurbsSurface quad_surface() {
std::vector<std::vector<Point3D>> grid = {
{Point3D(0,0,0), Point3D(0,1,0.5), Point3D(0,2,0)},
{Point3D(1,0,0.5), Point3D(1,1,1), Point3D(1,2,0.5)},
{Point3D(2,0,0), Point3D(2,1,0.5), Point3D(2,2,0)}
};
return NurbsSurface(grid,
{0,0,0,1,1,1}, {0,0,0,1,1,1},
{{1,1,1},{1,1,1},{1,1,1}}, 2, 2);
}
/// 创建圆柱面
static NurbsSurface cylinder_surface() {
double R = 1.0;
std::vector<std::vector<Point3D>> grid(5, std::vector<Point3D>(3));
for (int i = 0; i < 5; ++i) {
double angle = i * M_PI / 2.0;
double x = R * std::cos(angle);
double y = R * std::sin(angle);
for (int j = 0; j < 3; ++j) {
grid[i][j] = Point3D(x, y, j * 0.5);
}
}
std::vector<std::vector<double>> w(5, std::vector<double>(3, 1.0));
return NurbsSurface(grid,
{0,0,0,0,0.5,1,1,1,1},
{0,0,0,1,1,1}, w, 3, 2);
}
/// 沿 Z 偏移的平面
static NurbsSurface offset_plane(double z) {
std::vector<std::vector<Point3D>> grid = {
{Point3D(0,0,z), Point3D(0,1,z)},
{Point3D(1,0,z), Point3D(1,1,z)}
};
return NurbsSurface(grid, {0,0,1,1}, {0,0,1,1}, {{1,1},{1,1}}, 1, 1);
}
// ---------------------------------------------------------------------------
// Test: G3 Blend — G3 连续性过渡
// ---------------------------------------------------------------------------
TEST(ClassASurfacingTest, G3Blend_IdenticalPlanes) {
auto a = plane_surface();
auto b = offset_plane(0.0); // 重合平面
EdgeParams params;
params.edge_a = 3; // vmax of a
params.edge_b = 2; // vmin of b
params.blend_width = 0.2;
auto result = g3_blend(a, b, params);
// 过渡面应该非退化
auto [nu, nv] = result.blend_surface.num_control_points();
EXPECT_GT(nu, 0);
EXPECT_GT(nv, 0);
}
TEST(ClassASurfacingTest, G3Blend_ContinuityChecks) {
auto a = plane_surface();
auto b = offset_plane(0.0);
EdgeParams params;
params.blend_width = 0.15;
auto result = g3_blend(a, b, params);
// G0 位置连续
EXPECT_TRUE(result.g0_ok);
EXPECT_LT(result.max_position_error, 1e-3);
// G1 切平面连续
EXPECT_TRUE(result.g1_ok);
EXPECT_LT(result.max_tangent_error, 0.02);
}
TEST(ClassASurfacingTest, G3Blend_ResultHasBlendSurface) {
auto a = plane_surface();
auto b = offset_plane(1.0); // 间距 1
EdgeParams params;
params.edge_a = 3;
params.edge_b = 2;
params.blend_width = 0.5;
auto result = g3_blend(a, b, params);
auto [nu, nv] = result.blend_surface.num_control_points();
// 应生成有效的过渡曲面
EXPECT_GE(nu, 2);
EXPECT_EQ(nv, 4); // G3 需要 4 排控制点
}
// ---------------------------------------------------------------------------
// Test: Curvature Matching — 曲率匹配
// ---------------------------------------------------------------------------
TEST(ClassASurfacingTest, CurvatureMatching_FlatPlanes) {
auto a = plane_surface();
auto b = offset_plane(0.0);
CurvatureMatchOptions opts;
opts.samples_per_edge = 10;
opts.max_iterations = 20;
auto result = curvature_matching(a, b, opts);
// 平面曲率处处为0,匹配后应保持
EXPECT_LT(result.final_rms_curvature_diff, 1.0);
EXPECT_GT(result.iterations, 0);
}
TEST(ClassASurfacingTest, CurvatureMatching_ReturnsMatchedSurface) {
auto a = quad_surface();
auto b = quad_surface();
CurvatureMatchOptions opts;
opts.max_iterations = 5;
opts.tolerance = 1e-4;
auto result = curvature_matching(a, b, opts);
auto [nu, nv] = result.matched_surface_b.num_control_points();
EXPECT_EQ(nu, 3);
EXPECT_EQ(nv, 3);
}
TEST(ClassASurfacingTest, CurvatureMatching_DisplacementVector) {
auto a = plane_surface();
auto b = quad_surface();
CurvatureMatchOptions opts;
opts.max_iterations = 3;
auto result = curvature_matching(a, b, opts);
// 应有位移向量
EXPECT_FALSE(result.displacement.empty());
EXPECT_GE(result.displacement.size(), 1u);
}
// ---------------------------------------------------------------------------
// Test: Highlight Lines — 高光线分析
// ---------------------------------------------------------------------------
TEST(ClassASurfacingTest, HighlightLines_PlaneOutput) {
auto s = plane_surface();
std::vector<Vector3D> lights = {Vector3D(0,0,1)};
auto result = highlight_lines(s, lights, 10, 10, 6);
EXPECT_EQ(result.res_u, 10);
EXPECT_EQ(result.res_v, 10);
EXPECT_EQ(static_cast<int>(result.band_mask.size()), 1);
}
TEST(ClassASurfacingTest, HighlightLines_MultipleLights) {
auto s = quad_surface();
std::vector<Vector3D> lights = {
Vector3D(1,0,0), Vector3D(0,1,0), Vector3D(0,0,1)
};
auto result = highlight_lines(s, lights, 15, 15, 8);
EXPECT_EQ(static_cast<int>(result.band_mask.size()), 3);
EXPECT_EQ(static_cast<int>(result.continuity_scores.size()), 3);
EXPECT_GE(result.overall_score, 0.0);
EXPECT_LE(result.overall_score, 1.0);
}
TEST(ClassASurfacingTest, HighlightLines_FlatPlanePerfectScore) {
auto s = plane_surface();
std::vector<Vector3D> lights = {Vector3D(1,1,1)};
auto result = highlight_lines(s, lights, 20, 20, 10);
// 平面上高光线应连续
EXPECT_GT(result.continuity_scores[0], 0.9);
}
// ---------------------------------------------------------------------------
// Test: Reflection Lines — 反射线分析
// ---------------------------------------------------------------------------
TEST(ClassASurfacingTest, ReflectionLines_PlaneIsFair) {
auto s = plane_surface();
auto result = reflection_lines(s,
Point3D(5, 5, 5), Point3D(-5, -5, 10), 20, 20);
EXPECT_EQ(result.res_u, 20);
EXPECT_EQ(result.res_v, 20);
// 平面反射线应判定为光顺
EXPECT_TRUE(result.is_fair);
}
TEST(ClassASurfacingTest, ReflectionLines_DistortionBounds) {
auto s = quad_surface();
auto result = reflection_lines(s,
Point3D(3, 3, 5), Point3D(-3, -3, 8), 15, 15);
EXPECT_GE(result.max_distortion, 0.0);
EXPECT_GE(result.mean_distortion, 0.0);
EXPECT_LE(result.mean_distortion, result.max_distortion + 1e-9);
}
// ---------------------------------------------------------------------------
// Test: Iso-Photes — 等照度分析
// ---------------------------------------------------------------------------
TEST(ClassASurfacingTest, IsoPhotes_OutputDimensions) {
auto s = plane_surface();
auto result = iso_photes(s, 16);
EXPECT_EQ(result.res_u, 16);
EXPECT_EQ(result.res_v, 16);
EXPECT_EQ(static_cast<int>(result.illumination.size()), 17);
EXPECT_EQ(static_cast<int>(result.illumination[0].size()), 17);
}
TEST(ClassASurfacingTest, IsoPhotes_IlluminationRange) {
auto s = plane_surface();
auto result = iso_photes(s, 10);
for (size_t i = 0; i < result.illumination.size(); ++i) {
for (size_t j = 0; j < result.illumination[i].size(); ++j) {
double val = result.illumination[i][j];
EXPECT_GE(val, -1.1);
EXPECT_LE(val, 1.1);
}
}
}
TEST(ClassASurfacingTest, IsoPhotes_GradientAnalysis) {
auto s = quad_surface();
auto result = iso_photes(s, 20);
EXPECT_GE(result.grad_max, 0.0);
EXPECT_GE(result.grad_mean, 0.0);
}
// ---------------------------------------------------------------------------
// Test: Surface Diagnosis — 综合曲面诊断
// ---------------------------------------------------------------------------
TEST(ClassASurfacingTest, SurfaceDiagnosis_PlaneDiagnosis) {
auto s = plane_surface();
auto report = surface_diagnosis(s);
// 平面应该达到 A 级
EXPECT_EQ(report.grade, DiagnosisGrade::A_CLASS);
EXPECT_GT(report.overall_score, 80.0);
}
TEST(ClassASurfacingTest, SurfaceDiagnosis_ReportHasAllFields) {
auto s = quad_surface();
auto report = surface_diagnosis(s);
EXPECT_FALSE(report.gaussian_range.name.empty());
EXPECT_FALSE(report.mean_range.name.empty());
EXPECT_FALSE(report.highlight_score.name.empty());
EXPECT_FALSE(report.reflection_distortion.name.empty());
EXPECT_FALSE(report.isophote_gradient.name.empty());
EXPECT_FALSE(report.normal_jump.name.empty());
EXPECT_FALSE(report.recommendation.empty());
}
// ---------------------------------------------------------------------------
// Test: Shape Modification — 保形修改
// ---------------------------------------------------------------------------
TEST(ClassASurfacingTest, ShapeModification_SingleConstraint) {
auto s = plane_surface();
std::vector<ShapeConstraint> constraints;
ShapeConstraint c;
c.target_point = Point3D(0.5, 0.5, 0.5); // 提升0.5
c.u = 0.5;
c.v = 0.5;
c.weight = 1.0;
constraints.push_back(c);
auto result = shape_modification(s, constraints);
auto [nu, nv] = result.modified_surface.num_control_points();
EXPECT_EQ(nu, 2);
EXPECT_EQ(nv, 2);
// 控制点应有位移
EXPECT_GT(result.max_displacement, 0.0);
EXPECT_GE(result.energy_preserved_ratio, 0.0);
EXPECT_LE(result.energy_preserved_ratio, 1.1);
}
TEST(ClassASurfacingTest, ShapeModification_ConstraintPointMoves) {
auto s = plane_surface();
std::vector<ShapeConstraint> constraints;
ShapeConstraint c;
c.target_point = Point3D(0.3, 0.3, 2.0);
c.u = 0.3;
c.v = 0.3;
c.weight = 2.0;
constraints.push_back(c);
auto result = shape_modification(s, constraints);
// 修改后的曲面在约束点处应接近目标
auto pt = result.modified_surface.evaluate(0.3, 0.3);
EXPECT_GT(pt.z(), 0.1); // 应被提起
}
TEST(ClassASurfacingTest, ShapeModification_MultipleConstraints) {
auto s = quad_surface();
std::vector<ShapeConstraint> constraints;
for (int i = 0; i < 3; ++i) {
ShapeConstraint c;
c.u = 0.25 * (i + 1);
c.v = 0.5;
c.target_point = Point3D(c.u * 2, 1.0, 1.5 + i * 0.5);
c.weight = 1.0;
constraints.push_back(c);
}
auto result = shape_modification(s, constraints);
EXPECT_GE(result.iterations, 1);
EXPECT_GT(result.max_displacement, 0.0);
}