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
182 lines
6.1 KiB
C++
182 lines
6.1 KiB
C++
#include <gtest/gtest.h>
|
||
#include "vde/curves/surface_analysis.h"
|
||
#include "vde/curves/nurbs_curve.h"
|
||
#include "vde/curves/nurbs_surface.h"
|
||
#include "vde/brep/modeling.h"
|
||
#include <cmath>
|
||
|
||
using namespace vde::curves;
|
||
using namespace vde::core;
|
||
using namespace vde::brep;
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Helpers
|
||
// ---------------------------------------------------------------------------
|
||
|
||
/// Create a planar NURBS surface on XY plane over [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);
|
||
}
|
||
|
||
/// Create a simple NURBS curve (line)
|
||
static NurbsCurve line(const Point3D& a, const Point3D& b) {
|
||
return NurbsCurve({a, b}, {0, 0, 1, 1}, {1, 1}, 1);
|
||
}
|
||
|
||
/// Create a cubic NURBS curve (parabola-like)
|
||
static NurbsCurve cubic_curve() {
|
||
return NurbsCurve(
|
||
{Point3D(0,0,0), Point3D(1,2,0), Point3D(2,1,0), Point3D(3,0,0)},
|
||
{0,0,0,0,1,1,1,1},
|
||
{1,1,1,1},
|
||
3);
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Test: CurvatureMap
|
||
// ---------------------------------------------------------------------------
|
||
|
||
TEST(SurfaceAnalysisTest, CurvatureMap_PlaneIsDevelopable) {
|
||
auto surf = plane_surface();
|
||
auto cm = curvature_map(surf, 20, 20);
|
||
|
||
EXPECT_EQ(21, static_cast<int>(cm.grid.size()));
|
||
EXPECT_EQ(21, static_cast<int>(cm.grid[0].size()));
|
||
|
||
// Plane: Gaussian curvature ≈ 0, Mean curvature ≈ 0
|
||
EXPECT_NEAR(cm.min_gaussian, 0.0, 1e-9);
|
||
EXPECT_NEAR(cm.max_gaussian, 0.0, 1e-9);
|
||
EXPECT_NEAR(cm.min_mean, 0.0, 1e-9);
|
||
EXPECT_NEAR(cm.max_mean, 0.0, 1e-9);
|
||
|
||
EXPECT_TRUE(cm.is_developable());
|
||
}
|
||
|
||
TEST(SurfaceAnalysisTest, CurvatureMap_GridDimensions) {
|
||
auto surf = plane_surface();
|
||
auto cm = curvature_map(surf, 5, 8);
|
||
|
||
EXPECT_EQ(6, static_cast<int>(cm.grid.size())); // res_u+1
|
||
EXPECT_EQ(9, static_cast<int>(cm.grid[0].size())); // res_v+1
|
||
EXPECT_EQ(5, cm.res_u);
|
||
EXPECT_EQ(8, cm.res_v);
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Test: Zebra Stripe
|
||
// ---------------------------------------------------------------------------
|
||
|
||
TEST(SurfaceAnalysisTest, ZebraStripe_OutputDimensions) {
|
||
auto surf = plane_surface();
|
||
auto zs = zebra_stripe(surf, Vector3D(1, 0, 0), 15, 10);
|
||
|
||
EXPECT_EQ(16, static_cast<int>(zs.intensity.size()));
|
||
EXPECT_EQ(11, static_cast<int>(zs.intensity[0].size()));
|
||
EXPECT_EQ(15, zs.res_u);
|
||
EXPECT_EQ(10, zs.res_v);
|
||
|
||
// Check light direction is normalized
|
||
EXPECT_NEAR(zs.light_direction.norm(), 1.0, 1e-12);
|
||
}
|
||
|
||
TEST(SurfaceAnalysisTest, ZebraStripe_IntensityRange) {
|
||
auto surf = plane_surface();
|
||
auto zs = zebra_stripe(surf, Vector3D(0, 0, 1), 10, 10);
|
||
|
||
for (size_t i = 0; i < zs.intensity.size(); ++i) {
|
||
for (size_t j = 0; j < zs.intensity[i].size(); ++j) {
|
||
double val = zs.intensity[i][j];
|
||
EXPECT_GE(val, 0.0);
|
||
EXPECT_LE(val, 1.0);
|
||
}
|
||
}
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Test: Curvature Comb
|
||
// ---------------------------------------------------------------------------
|
||
|
||
TEST(SurfaceAnalysisTest, CurvatureComb_StraightLine) {
|
||
auto c = line(Point3D(0, 0, 0), Point3D(10, 0, 0));
|
||
auto comb = curvature_comb(c, 20);
|
||
|
||
EXPECT_EQ(21, static_cast<int>(comb.points.size()));
|
||
// Straight line has zero curvature
|
||
EXPECT_NEAR(comb.min_curvature, 0.0, 1e-12);
|
||
EXPECT_NEAR(comb.max_curvature, 0.0, 1e-12);
|
||
}
|
||
|
||
TEST(SurfaceAnalysisTest, CurvatureComb_NonzeroCurvature) {
|
||
auto c = cubic_curve();
|
||
auto comb = curvature_comb(c, 100);
|
||
|
||
EXPECT_EQ(101, static_cast<int>(comb.points.size()));
|
||
// Cubic curve has non-zero curvature
|
||
EXPECT_GT(comb.max_curvature, 0.0);
|
||
// Scale should be auto-computed
|
||
EXPECT_GT(comb.scale, 0.0);
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Test: Deviation Analysis
|
||
// ---------------------------------------------------------------------------
|
||
|
||
TEST(SurfaceAnalysisTest, DeviationAnalysis_IdenticalPlanes) {
|
||
auto s1 = plane_surface();
|
||
auto s2 = plane_surface();
|
||
auto result = deviation_analysis(s1, s2, 10);
|
||
|
||
EXPECT_EQ(100u, result.sample_count);
|
||
// Identical planes: deviation ≈ 0
|
||
EXPECT_NEAR(result.rms, 0.0, 1e-6);
|
||
EXPECT_NEAR(result.mean_absolute, 0.0, 1e-6);
|
||
}
|
||
|
||
TEST(SurfaceAnalysisTest, DeviationAnalysis_OffsetPlanes) {
|
||
auto s1 = plane_surface();
|
||
|
||
// Same plane shifted up by 1 in Z
|
||
std::vector<std::vector<Point3D>> grid2 = {
|
||
{Point3D(0,0,1), Point3D(0,1,1)},
|
||
{Point3D(1,0,1), Point3D(1,1,1)}
|
||
};
|
||
auto s2 = NurbsSurface(grid2, {0,0,1,1}, {0,0,1,1},
|
||
{{1,1},{1,1}}, 1, 1);
|
||
|
||
auto result = deviation_analysis(s1, s2, 10);
|
||
|
||
EXPECT_NEAR(result.max_positive, 1.0, 0.05);
|
||
EXPECT_NEAR(result.rms, 1.0, 0.05);
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Test: Draft Face Angle
|
||
// ---------------------------------------------------------------------------
|
||
|
||
TEST(SurfaceAnalysisTest, DraftAngle_HorizontalPlaneVerticalPull) {
|
||
auto surf = plane_surface();
|
||
// Horizontal plane (normal = +Z), vertical pull (+Z) → angle = -π/2
|
||
// (face normal parallel to pull: face is perpendicular to opening)
|
||
double angle = draft_face_angle(surf, 0.5, 0.5, Vector3D(0, 0, 1));
|
||
EXPECT_NEAR(angle, -M_PI_2, 1e-6);
|
||
}
|
||
|
||
TEST(SurfaceAnalysisTest, DraftAngle_PullPerpendicularToNormal) {
|
||
auto surf = plane_surface();
|
||
// Horizontal plane (normal = +Z), pull in X → face parallel to pull → angle ≈ 0
|
||
double angle = draft_face_angle(surf, 0.5, 0.5, Vector3D(1, 0, 0));
|
||
EXPECT_NEAR(angle, 0.0, 1e-6);
|
||
}
|
||
|
||
TEST(SurfaceAnalysisTest, DraftAngle_BrepModel) {
|
||
auto box = make_box(10, 10, 10);
|
||
// Top face has normal ≈ +Z, pull +Z → angle ≈ -π/2
|
||
double angle = draft_face_angle(box, 0, Vector3D(0, 0, 1), 5);
|
||
EXPECT_NEAR(std::abs(angle), M_PI_2, 0.01);
|
||
}
|