Files
ViewDesignEngine/tests/curves/test_generative_surfaces.cpp
茂之钳 bb0029234f
Build & Test / build-and-test (push) Waiting to run
Build & Test / python-bindings (push) Blocked by required conditions
CI / Build & Test (push) Failing after 31s
CI / Release Build (push) Failing after 38s
feat(v12): generative surfaces (Sweep/Loft/Net) + curve tools + surface analysis
M1 — Generative Surfaces:
- sweep_surface: Explicit/Spine/TwoGuides modes
- loft_surface: multi-section interpolation + guide + tangent constraints
- net_surface: bidirectional curve grid → NURBS

M3 — Curve Tools + Analysis:
- curve_tools: project_curve, parallel_curve, connect_curve(G1-G3), helix, isoparametric
- surface_analysis: inflection_lines, checker_mapping, surface_checker_report (A/B/C/D)
- 14/14 tests passed

Fixed: constraint_solver.h duplicate declaration, fea_mesh.h comment syntax

Pending: M2 surface editing (retrying)
2026-07-27 09:08:01 +08:00

341 lines
10 KiB
C++

#include <gtest/gtest.h>
#include "vde/curves/generative_surfaces.h"
#include "vde/curves/nurbs_curve.h"
#include "vde/core/point.h"
#include <cmath>
using namespace vde::curves;
using namespace vde::core;
// ── 辅助:创建直线 NURBS ──
static NurbsCurve make_line(const Point3D& a, const Point3D& b) {
return NurbsCurve({a, b}, {0, 0, 1, 1}, {1, 1}, 1);
}
// ── 辅助:创建圆弧 profile ──
static NurbsCurve make_arc_profile() {
double r = 1.0;
double w = std::sqrt(2.0) / 2.0;
return NurbsCurve(
{Point3D(0, r, 0), Point3D(r, r, 0), Point3D(r, 0, 0)},
{0, 0, 0, 0.5, 1, 1, 1},
{1, w, 1, w, 1, w, 1},
2
);
}
// ============================================================================
// Sweep 测试
// ============================================================================
TEST(GenerativeSurfacesTest, SweepExplicit_Basic) {
// 圆形 profile 沿直线扫描 → 圆柱面
NurbsCurve profile(
{Point3D(0, 1, 0), Point3D(1, 1, 0), Point3D(1, 0, 0)},
{0, 0, 0, 1, 1, 1},
{1, 1, 1},
2
);
NurbsCurve path = make_line({0, 0, 0}, {0, 0, 5});
SweepOption opt;
opt.type = SweepType::Explicit;
opt.samples = 8;
auto surf = sweep_surface(profile, path, opt);
EXPECT_EQ(surf.degree_u(), 2);
EXPECT_GE(surf.num_control_points()[0], 3);
EXPECT_GE(surf.num_control_points()[1], 3);
// 验证曲面在参数域中点可求值
auto p = surf.evaluate(0.5, 0.5);
EXPECT_GE(p.x(), 0.0);
EXPECT_LE(p.z(), 5.0);
}
TEST(GenerativeSurfacesTest, SweepExplicit_StraightPath) {
// 矩形 profile 沿 X 轴扫描
NurbsCurve profile = make_line({0, 0, 0}, {0, 0, 2});
NurbsCurve path = make_line({0, 0, 0}, {10, 0, 0});
SweepOption opt;
opt.type = SweepType::Explicit;
opt.samples = 4;
auto surf = sweep_surface(profile, path, opt);
// 验证曲面两端
auto p0 = surf.evaluate(0.0, 0.0);
auto p1 = surf.evaluate(1.0, 1.0);
EXPECT_NEAR(p0.x(), 0.0, 1e-6);
EXPECT_NEAR(p1.x(), 10.0, 1e-6);
EXPECT_GE(p1.z(), 0.0);
}
TEST(GenerativeSurfacesTest, SweepExplicit_VaryingSamples) {
NurbsCurve profile = make_line({0, 1, 0}, {0, -1, 0});
NurbsCurve path = make_line({0, 0, 0}, {0, 0, 3});
SweepOption opt;
opt.type = SweepType::Explicit;
opt.samples = 16;
auto surf = sweep_surface(profile, path, opt);
// 控制点数量应随采样增加
int n_u = surf.num_control_points()[0];
EXPECT_GT(n_u, 4);
// 曲面可正常求值
auto mid = surf.evaluate(0.5, 0.5);
EXPECT_NEAR(mid.x(), 0.0, 1e-6);
EXPECT_NEAR(mid.z(), 1.5, 1.0);
}
TEST(GenerativeSurfacesTest, SweepSpine_Basic) {
// 使用独立脊线的扫描
NurbsCurve profile = make_line({0, 1, 0}, {0, -1, 0});
NurbsCurve path = make_line({0, 0, 0}, {5, 0, 0});
// 脊线偏离路径,用于扭转控制
NurbsCurve spine = make_line({0, 0, 0}, {5, 0, 2});
SweepOption opt;
opt.type = SweepType::Spine;
opt.spine = spine;
opt.samples = 8;
auto surf = sweep_surface(profile, path, opt);
EXPECT_GE(surf.num_control_points()[0], 3);
EXPECT_GE(surf.num_control_points()[1], 2);
// 验证曲面可求值
auto p = surf.evaluate(0.5, 0.5);
EXPECT_FALSE(std::isnan(p.x()));
EXPECT_FALSE(std::isnan(p.y()));
EXPECT_FALSE(std::isnan(p.z()));
}
TEST(GenerativeSurfacesTest, SweepTwoGuides_Basic) {
// 双引导线扫描:两条引导线间距变化控制缩放
NurbsCurve profile = make_line({0, 1, 0}, {0, -1, 0});
NurbsCurve path = make_line({0, 0, 0}, {5, 0, 0});
// 两条引导线逐渐分开
NurbsCurve guide1 = make_line({0, 0.5, 0}, {5, 2, 0});
NurbsCurve guide2 = make_line({0, -0.5, 0}, {5, -2, 0});
SweepOption opt;
opt.type = SweepType::TwoGuides;
opt.guide1 = guide1;
opt.guide2 = guide2;
opt.samples = 8;
auto surf = sweep_surface(profile, path, opt);
EXPECT_GE(surf.num_control_points()[0], 3);
EXPECT_GE(surf.num_control_points()[1], 2);
// 曲面起点和终点宽度应不同(缩放生效)
auto p_start = surf.evaluate(0.0, 0.5);
auto p_end = surf.evaluate(1.0, 0.5);
EXPECT_GE(std::abs(p_end.y()) - std::abs(p_start.y()), -0.5);
}
TEST(GenerativeSurfacesTest, Sweep_DefaultOption) {
// 默认选项(Explicit 模式)
NurbsCurve profile = make_line({0, 1, 0}, {0, -1, 0});
NurbsCurve path = make_line({0, 0, 0}, {3, 0, 0});
auto surf = sweep_surface(profile, path);
EXPECT_GE(surf.num_control_points()[0], 3);
auto p = surf.evaluate(0.5, 0.5);
EXPECT_FALSE(std::isnan(p.x()));
}
// ============================================================================
// Loft 测试
// ============================================================================
TEST(GenerativeSurfacesTest, Loft_Basic) {
// 两条平行截面放样
NurbsCurve s1 = make_line({0, 0, 0}, {2, 0, 0});
NurbsCurve s2 = make_line({0, 2, 5}, {2, 2, 5});
auto surf = loft_surface({s1, s2});
EXPECT_GE(surf.num_control_points()[0], 2);
EXPECT_GE(surf.num_control_points()[1], 2);
// 验证曲面插值首尾截面
auto p_bottom = surf.evaluate(0.5, 0.0);
EXPECT_NEAR(p_bottom.z(), 0.0, 1e-6);
auto p_top = surf.evaluate(0.5, 1.0);
EXPECT_NEAR(p_top.z(), 5.0, 1e-6);
}
TEST(GenerativeSurfacesTest, Loft_ThreeSections) {
NurbsCurve s1 = make_line({0, 0, 0}, {2, 0, 0});
NurbsCurve s2 = make_line({0, 1, 3}, {2, 1, 3});
NurbsCurve s3 = make_line({0, 0, 6}, {2, 0, 6});
auto surf = loft_surface({s1, s2, s3});
EXPECT_GE(surf.num_control_points()[0], 2);
EXPECT_GE(surf.num_control_points()[1], 2);
// 中间截面应偏离直线
auto p_mid = surf.evaluate(0.5, 0.5);
EXPECT_NEAR(p_mid.z(), 3.0, 1.0);
EXPECT_GT(p_mid.y(), 0.0);
}
TEST(GenerativeSurfacesTest, Loft_WithGuides) {
NurbsCurve s1 = make_line({0, 0, 0}, {2, 0, 0});
NurbsCurve s2 = make_line({0, 0, 5}, {2, 0, 5});
NurbsCurve s3 = make_line({0, 0, 10}, {2, 0, 10});
// 引导线:弯曲的脊线
NurbsCurve guide(
{Point3D(0, 1, 2.5), Point3D(1, 0, 5), Point3D(0, -1, 7.5)},
{0, 0, 0, 1, 1, 1},
{1, 1, 1},
2
);
auto surf = loft_surface({s1, s2, s3}, {guide});
EXPECT_GE(surf.num_control_points()[0], 2);
EXPECT_GE(surf.num_control_points()[1], 2);
auto p = surf.evaluate(0.5, 0.5);
EXPECT_FALSE(std::isnan(p.x()));
}
TEST(GenerativeSurfacesTest, Loft_WithTangents) {
NurbsCurve s1 = make_line({0, 0, 0}, {2, 0, 0});
NurbsCurve s2 = make_line({0, 0, 5}, {2, 0, 5});
LoftConstraint cons;
cons.start_tangent = Vector3D(0, 0, 1); // 垂直向上
cons.end_tangent = Vector3D(0, 0, 1); // 垂直向上
auto surf = loft_surface({s1, s2}, {}, cons);
EXPECT_GE(surf.num_control_points()[0], 2);
EXPECT_GE(surf.num_control_points()[1], 2);
// 曲面应能正常求值
auto p = surf.evaluate(0.5, 0.3);
EXPECT_FALSE(std::isnan(p.x()));
}
TEST(GenerativeSurfacesTest, Loft_InvalidInput) {
// 单个截面应抛出异常
NurbsCurve s1 = make_line({0, 0, 0}, {1, 0, 0});
EXPECT_THROW((void)loft_surface({s1}), std::invalid_argument);
}
// ============================================================================
// Net 测试
// ============================================================================
TEST(GenerativeSurfacesTest, Net_Basic) {
// 双向曲线网格:u 向和 v 向各 2 条线
NurbsCurve u0 = make_line({0, 0, 0}, {5, 0, 0});
NurbsCurve u1 = make_line({0, 2, 0}, {5, 2, 0});
NurbsCurve v0 = make_line({0, 0, 0}, {0, 2, 0});
NurbsCurve v1 = make_line({5, 0, 0}, {5, 2, 0});
auto surf = net_surface({u0, u1}, {v0, v1});
EXPECT_GE(surf.num_control_points()[0], 2);
EXPECT_GE(surf.num_control_points()[1], 2);
// 曲面应在 XY 平面内
auto p = surf.evaluate(0.5, 0.5);
EXPECT_NEAR(p.z(), 0.0, 1e-6);
EXPECT_GE(p.x(), 0.0);
EXPECT_GE(p.y(), 0.0);
}
TEST(GenerativeSurfacesTest, Net_UnequalCount) {
// u 向 3 条,v 向 2 条
NurbsCurve u0 = make_line({0, 0, 0}, {5, 0, 0});
NurbsCurve u1 = make_line({0, 1, 0}, {5, 1, 0});
NurbsCurve u2 = make_line({0, 2, 0}, {5, 2, 0});
NurbsCurve v0 = make_line({0, 0, 0}, {0, 2, 0});
NurbsCurve v1 = make_line({5, 0, 0}, {5, 2, 0});
auto surf = net_surface({u0, u1, u2}, {v0, v1});
EXPECT_EQ(surf.num_control_points()[0], 3);
EXPECT_EQ(surf.num_control_points()[1], 2);
}
TEST(GenerativeSurfacesTest, Net_3DCurves) {
// 三维网格曲面
NurbsCurve u0(
{Point3D(0, 0, 0), Point3D(1, 0, 1), Point3D(2, 0, 0)},
{0, 0, 0, 1, 1, 1},
{1, 1, 1},
2
);
NurbsCurve u1(
{Point3D(0, 2, 0), Point3D(1, 2, 1), Point3D(2, 2, 0)},
{0, 0, 0, 1, 1, 1},
{1, 1, 1},
2
);
NurbsCurve v0 = make_line({0, 0, 0}, {0, 2, 0});
NurbsCurve v1 = make_line({2, 0, 0}, {2, 2, 0});
auto surf = net_surface({u0, u1}, {v0, v1});
EXPECT_GE(surf.num_control_points()[0], 2);
EXPECT_GE(surf.num_control_points()[1], 2);
auto p = surf.evaluate(0.5, 0.5);
EXPECT_FALSE(std::isnan(p.x()));
EXPECT_GE(p.z(), 0.0);
}
TEST(GenerativeSurfacesTest, Net_InvalidInput) {
NurbsCurve u0 = make_line({0, 0, 0}, {1, 0, 0});
EXPECT_THROW((void)net_surface({u0}, {u0}), std::invalid_argument);
NurbsCurve v0 = make_line({0, 0, 0}, {1, 0, 0});
EXPECT_THROW((void)net_surface({u0, u0}, {v0}), std::invalid_argument);
}
// ============================================================================
// 集成测试:组合使用
// ============================================================================
TEST(GenerativeSurfacesTest, Integration_SweepThenEvaluate) {
// 验证扫掠后曲面求值一致性
NurbsCurve profile = make_line({0, 1, 0}, {0, -1, 0});
NurbsCurve path = make_line({0, 0, 0}, {10, 0, 0});
SweepOption opt;
opt.type = SweepType::Explicit;
opt.samples = 10;
auto surf = sweep_surface(profile, path, opt);
// 曲面法向应在 XY 平面内(近似)
for (double u = 0.0; u <= 1.0; u += 0.25) {
for (double v = 0.0; v <= 1.0; v += 0.25) {
auto pt = surf.evaluate(u, v);
EXPECT_FALSE(std::isnan(pt.x()));
EXPECT_FALSE(std::isnan(pt.y()));
EXPECT_FALSE(std::isnan(pt.z()));
}
}
}