Files
ViewDesignEngine/tests/curves/test_surface_editing.cpp
T
茂之钳 bb0029234f
CI / Build & Test (push) Failing after 31s
CI / Release Build (push) Failing after 38s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled
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

250 lines
8.2 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.
/**
* @file test_surface_editing.cpp
* @brief 曲面编辑三件套测试:match_surface / shape_fillet / control_point_edit
*/
#include <gtest/gtest.h>
#include "vde/curves/surface_editing.h"
#include "vde/curves/control_point_edit.h"
#include "vde/curves/nurbs_surface.h"
#include "vde/core/point.h"
#include <cmath>
namespace vde::curves {
namespace test {
using core::Point3D;
using core::Vector3D;
// ── Helper: create a simple biquadratic plane surface ──
NurbsSurface make_test_plane() {
// 3×3 control grid on z=0 plane
std::vector<std::vector<Point3D>> grid = {
{{0, 0, 0}, {1, 0, 0}, {2, 0, 0}},
{{0, 1, 0}, {1, 1, 0}, {2, 1, 0}},
{{0, 2, 0}, {1, 2, 0}, {2, 2, 0}}
};
return NurbsSurface(grid,
{0, 0, 0, 1, 1, 1}, // u-knots, degree=2
{0, 0, 0, 1, 1, 1}, // v-knots, degree=2
{}, 2, 2); // weights (empty = all 1.0)
}
// ── Helper: create a slightly curved surface (degree 2×2) ──
NurbsSurface make_test_curved() {
// 3×3 grid, slightly curved in z
std::vector<std::vector<Point3D>> grid = {
{{0, 0, 0}, {1, 0, 0.5}, {2, 0, 0}},
{{0, 1, 0.5}, {1, 1, 1.0}, {2, 1, 0.5}},
{{0, 2, 0}, {1, 2, 0.5}, {2, 2, 0}}
};
return NurbsSurface(grid,
{0, 0, 0, 1, 1, 1}, {0, 0, 0, 1, 1, 1}, {}, 2, 2);
}
// ═══════════════════════════════════════════════════════════
// match_surface 测试
// ═══════════════════════════════════════════════════════════
TEST(SurfaceEditingTest, MatchSurfaceG0Planar) {
// Two identical planes on z=0 — matching should produce no error
auto plane_a = make_test_plane();
auto plane_b = make_test_plane();
MatchSurfaceOptions opts;
opts.continuity = ContinuityLevel::G0;
opts.boundary_samples = 10;
auto result = match_surface(plane_b, plane_a, BoundaryEdge::VMin, opts);
EXPECT_TRUE(result.g0_ok);
EXPECT_LT(result.max_position_error, 1e-4);
EXPECT_EQ(result.rows_adjusted, 1);
EXPECT_EQ(result.control_displacements.size(), 9u); // 3×3 grid
}
TEST(SurfaceEditingTest, MatchSurfaceG0DifferentSurfaces) {
// Two slightly different curved surfaces — match VMin edge
auto surf_a = make_test_curved();
// Modify a few control points to make it different
auto surf_b = make_test_curved();
MatchSurfaceOptions opts;
opts.continuity = ContinuityLevel::G0;
opts.boundary_samples = 10;
auto result = match_surface(surf_b, surf_a, BoundaryEdge::VMin, opts);
// Should produce a valid surface
auto [nu, nv] = result.matched_surface.num_control_points();
EXPECT_GE(nu, 1);
EXPECT_GE(nv, 1);
// Control points should be adjusted
EXPECT_GT(result.control_displacements.size(), 0u);
}
TEST(SurfaceEditingTest, MatchSurfaceG1Planar) {
auto plane_a = make_test_plane();
auto plane_b = make_test_plane();
MatchSurfaceOptions opts;
opts.continuity = ContinuityLevel::G1;
opts.boundary_samples = 10;
auto result = match_surface(plane_b, plane_a, BoundaryEdge::VMin, opts);
EXPECT_TRUE(result.g0_ok);
EXPECT_TRUE(result.g1_ok);
EXPECT_EQ(result.rows_adjusted, 1);
}
TEST(SurfaceEditingTest, MatchSurfaceG1DifferentSurfaces) {
auto surf_a = make_test_curved();
auto surf_b = make_test_curved();
MatchSurfaceOptions opts;
opts.continuity = ContinuityLevel::G1;
opts.boundary_samples = 10;
auto result = match_surface(surf_b, surf_a, BoundaryEdge::VMin, opts);
// Check that errors are computed
EXPECT_GE(result.max_position_error, 0.0);
EXPECT_GE(result.max_tangent_error, 0.0);
EXPECT_EQ(result.rows_adjusted, 1);
}
TEST(SurfaceEditingTest, MatchSurfaceG2Planar) {
auto plane_a = make_test_plane();
auto plane_b = make_test_plane();
MatchSurfaceOptions opts;
opts.continuity = ContinuityLevel::G2;
opts.boundary_samples = 10;
auto result = match_surface(plane_b, plane_a, BoundaryEdge::VMin, opts);
EXPECT_TRUE(result.g0_ok);
EXPECT_EQ(result.rows_adjusted, 2);
}
TEST(SurfaceEditingTest, MatchSurfaceG3Planar) {
auto plane_a = make_test_plane();
auto plane_b = make_test_plane();
MatchSurfaceOptions opts;
opts.continuity = ContinuityLevel::G3;
opts.boundary_samples = 10;
auto result = match_surface(plane_b, plane_a, BoundaryEdge::VMin, opts);
EXPECT_TRUE(result.g0_ok);
EXPECT_EQ(result.rows_adjusted, 3); // G3 adjusts 3 rows
}
TEST(SurfaceEditingTest, MatchSurfaceAllEdges) {
auto plane_a = make_test_plane();
auto plane_b = make_test_plane();
MatchSurfaceOptions opts;
opts.continuity = ContinuityLevel::G1;
// Test all 4 boundary edges
for (int e = 0; e < 4; ++e) {
BoundaryEdge edge = static_cast<BoundaryEdge>(e);
auto result = match_surface(plane_b, plane_a, edge, opts);
EXPECT_TRUE(result.g0_ok) << "Failed on edge " << e;
}
}
TEST(SurfaceEditingTest, MatchSurfaceExplicitRows) {
auto plane_a = make_test_plane();
auto plane_b = make_test_plane();
MatchSurfaceOptions opts;
opts.continuity = ContinuityLevel::G2;
opts.rows_to_adjust = 1; // override, only 1 row
opts.boundary_samples = 10;
auto result = match_surface(plane_b, plane_a, BoundaryEdge::VMin, opts);
EXPECT_EQ(result.rows_adjusted, 1);
}
// ═══════════════════════════════════════════════════════════
// control_point_edit 测试
// ═══════════════════════════════════════════════════════════
TEST(SurfaceEditingTest, ControlPointEditSingle) {
auto surf = make_test_plane();
std::vector<std::pair<int,int>> indices = {{1, 1}}; // center control point
std::vector<Point3D> new_pos = {{1, 1, 5}}; // lift up in z
auto result = control_point_edit(surf, indices, new_pos);
EXPECT_EQ(result.points_modified, 1);
EXPECT_GT(result.max_displacement, 4.0); // moved ~5 units in z
EXPECT_TRUE(result.weights_preserved);
// Verify the control point was moved
auto modified_cp = result.modified_surface.control_points();
EXPECT_NEAR(modified_cp[1][1].z(), 5.0, 1e-6);
// Other control points unchanged
EXPECT_NEAR(modified_cp[0][0].z(), 0.0, 1e-6);
}
TEST(SurfaceEditingTest, ControlPointEditMultiple) {
auto surf = make_test_plane();
std::vector<std::pair<int,int>> indices = {{0, 0}, {2, 2}};
std::vector<Point3D> new_pos = {{0, 0, 3}, {3, 3, 3}};
auto result = control_point_edit(surf, indices, new_pos);
EXPECT_EQ(result.points_modified, 2);
EXPECT_GT(result.max_displacement, 2.0);
auto cp = result.modified_surface.control_points();
EXPECT_NEAR(cp[0][0].z(), 3.0, 1e-6);
EXPECT_NEAR(cp[2][2].z(), 3.0, 1e-6);
// Center unchanged
EXPECT_NEAR(cp[1][1].z(), 0.0, 1e-6);
}
TEST(SurfaceEditingTest, ControlPointEditEmpty) {
auto surf = make_test_plane();
std::vector<std::pair<int,int>> indices;
std::vector<Point3D> new_pos;
auto result = control_point_edit(surf, indices, new_pos);
EXPECT_EQ(result.points_modified, 0);
EXPECT_NEAR(result.max_displacement, 0.0, 1e-12);
// Should return a valid copy of original
auto cp = result.modified_surface.control_points();
EXPECT_EQ(cp.size(), 3u);
EXPECT_NEAR(cp[0][0].z(), 0.0, 1e-6);
}
TEST(SurfaceEditingTest, ControlPointEditPreservesKnots) {
auto surf = make_test_plane();
std::vector<std::pair<int,int>> indices = {{0, 0}};
std::vector<Point3D> new_pos = {{-1, -1, 0}};
auto result = control_point_edit(surf, indices, new_pos);
// Knot vectors preserved
const auto& ku = result.modified_surface.knots_u();
const auto& kv = result.modified_surface.knots_v();
EXPECT_EQ(ku.size(), 6u);
EXPECT_EQ(kv.size(), 6u);
EXPECT_EQ(result.modified_surface.degree_u(), 2);
EXPECT_EQ(result.modified_surface.degree_v(), 2);
}
} // namespace test
} // namespace vde::curves