#include #include "vde/curves/surface_continuity.h" #include "vde/curves/nurbs_curve.h" #include "vde/curves/nurbs_surface.h" #include using namespace vde::curves; using namespace vde::core; // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- /// Create a degree-1 NURBS line from a to b static NurbsCurve line(const Point3D& a, const Point3D& b) { return NurbsCurve({a, b}, {0, 0, 1, 1}, {1, 1}, 1); } /// Create a planar NURBS surface on XY plane over [0,1]×[0,1] static NurbsSurface plane_surface() { std::vector> 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 surface from a 2×2 grid static NurbsSurface simple_surface(const std::vector>& grid, int du=1, int dv=1) { int rows = static_cast(grid.size()); int cols = static_cast(grid.empty() ? 0 : grid[0].size()); std::vector ku(rows + du + 1, 0.0); std::vector kv(cols + dv + 1, 0.0); for (int i = 0; i <= du; ++i) ku[i] = 0.0; for (size_t i = du + 1; i < ku.size() - du - 1; ++i) ku[i] = static_cast(i - du) / (ku.size() - 2*du - 1); for (size_t i = ku.size() - du - 1; i < ku.size(); ++i) ku[i] = 1.0; for (int i = 0; i <= dv; ++i) kv[i] = 0.0; for (size_t i = dv + 1; i < kv.size() - dv - 1; ++i) kv[i] = static_cast(i - dv) / (kv.size() - 2*dv - 1); for (size_t i = kv.size() - dv - 1; i < kv.size(); ++i) kv[i] = 1.0; std::vector> w(rows, std::vector(cols, 1.0)); return NurbsSurface(grid, ku, kv, w, du, dv); } // --------------------------------------------------------------------------- // Test: to_string // --------------------------------------------------------------------------- TEST(ContinuityTest, ToString) { EXPECT_STREQ("G0", to_string(ContinuityLevel::G0)); EXPECT_STREQ("G1", to_string(ContinuityLevel::G1)); EXPECT_STREQ("G2", to_string(ContinuityLevel::G2)); EXPECT_STREQ("G3", to_string(ContinuityLevel::G3)); EXPECT_STREQ("None", to_string(ContinuityLevel::None)); } // --------------------------------------------------------------------------- // Test: G0 — Position continuity on lines // --------------------------------------------------------------------------- TEST(ContinuityTest, G0_SameCurveContinuous) { // Two identical line segments should be G1 (same tangent direction) auto a = line(Point3D(0, 0, 0), Point3D(1, 0, 0)); auto b = line(Point3D(1, 0, 0), Point3D(2, 0, 0)); auto level = continuity_type(a, b); EXPECT_GE(static_cast(level), static_cast(ContinuityLevel::G1)); } TEST(ContinuityTest, G0_EndpointsMatch) { auto a = line(Point3D(0, 0, 0), Point3D(1, 0, 0)); auto b = line(Point3D(1, 0, 0), Point3D(1, 1, 0)); auto level = continuity_type(a, b); EXPECT_GE(static_cast(level), static_cast(ContinuityLevel::G0)); } TEST(ContinuityTest, G0_NoConnection) { auto a = line(Point3D(0, 0, 0), Point3D(1, 0, 0)); auto b = line(Point3D(5, 5, 5), Point3D(6, 6, 6)); auto level = continuity_type(a, b); EXPECT_EQ(ContinuityLevel::None, level); } // --------------------------------------------------------------------------- // Test: G1 — Tangent continuity // --------------------------------------------------------------------------- TEST(ContinuityTest, G1_CollinearLines) { // Two collinear lines → G0 position, G1 tangent auto a = line(Point3D(0, 0, 0), Point3D(1, 0, 0)); auto b = line(Point3D(1, 0, 0), Point3D(2, 0, 0)); // Check at the connection point with explicit t values auto level = continuity_type(a, 1.0, b, 0.0); EXPECT_GE(static_cast(level), static_cast(ContinuityLevel::G1)); } TEST(ContinuityTest, G1_SharpCornerG0Only) { // Two lines meeting at a right angle → only G0 auto a = line(Point3D(0, 0, 0), Point3D(1, 0, 0)); auto b = line(Point3D(1, 0, 0), Point3D(1, 1, 0)); auto level = continuity_type(a, 1.0, b, 0.0); EXPECT_EQ(ContinuityLevel::G0, level); } // --------------------------------------------------------------------------- // Test: G2 — Curvature continuity on curves // --------------------------------------------------------------------------- TEST(ContinuityTest, G2_StraightLinesG3) { // Two collinear straight lines have zero curvature everywhere → G3 auto a = line(Point3D(0, 0, 0), Point3D(1, 0, 0)); auto b = line(Point3D(1, 0, 0), Point3D(3, 0, 0)); auto level = continuity_type(a, 1.0, b, 0.0); // Straight lines have d2=0, curvature=0, so they trivially match → G3 EXPECT_EQ(ContinuityLevel::G3, level); } TEST(ContinuityTest, G0_DisconnectedReturnsNone) { auto a = line(Point3D(0, 0, 0), Point3D(1, 0, 0)); auto b = line(Point3D(10, 10, 10), Point3D(11, 11, 11)); auto level = continuity_type(a, 0.5, b, 0.5); EXPECT_EQ(ContinuityLevel::None, level); } // --------------------------------------------------------------------------- // Test: Surface Continuity // --------------------------------------------------------------------------- TEST(SurfaceContinuityTest, IdenticalPlanes) { // Two identical flat planes → should be G3 auto s1 = plane_surface(); auto s2 = plane_surface(); auto level = surface_continuity(s1, s2, 10); EXPECT_GE(static_cast(level), static_cast(ContinuityLevel::G3)); } TEST(SurfaceContinuityTest, CoplanarSurfacesEdgeMatch) { // Two adjacent coplanar surfaces sharing an edge std::vector> g1 = { {Point3D(0,0,0), Point3D(0,1,0)}, {Point3D(1,0,0), Point3D(1,1,0)} }; std::vector> g2 = { {Point3D(1,0,0), Point3D(1,1,0)}, {Point3D(2,0,0), Point3D(2,1,0)} }; auto s1 = simple_surface(g1); auto s2 = simple_surface(g2); auto level = surface_continuity(s1, s2, 10); EXPECT_GE(static_cast(level), static_cast(ContinuityLevel::G3)); } TEST(SurfaceContinuityTest, DisconnectedSurfaces) { std::vector> g1 = { {Point3D(0,0,0), Point3D(0,1,0)}, {Point3D(1,0,0), Point3D(1,1,0)} }; std::vector> g2 = { {Point3D(10,0,0), Point3D(10,1,0)}, {Point3D(11,0,0), Point3D(11,1,0)} }; auto s1 = simple_surface(g1); auto s2 = simple_surface(g2); auto level = surface_continuity(s1, s2, 10); EXPECT_EQ(ContinuityLevel::None, level); } TEST(SurfaceContinuityTest, ContinuityReport) { auto s1 = plane_surface(); auto s2 = plane_surface(); auto report = surface_continuity_report(s1, s2, 20); EXPECT_GE(static_cast(report.worst), static_cast(ContinuityLevel::G3)); EXPECT_NEAR(report.g1_ratio, 1.0, 0.01); EXPECT_NEAR(report.g2_ratio, 1.0, 0.01); } // --------------------------------------------------------------------------- // Test: Edge parameter overload // --------------------------------------------------------------------------- TEST(SurfaceContinuityTest, ExplicitEdgeParametersPlane) { auto s1 = plane_surface(); auto s2 = plane_surface(); // Same physical edge (u=1) on both identical surfaces auto level = surface_continuity( s1, s2, {{1.0, 0.0}, {1.0, 1.0}}, // s1: u=1 edge, v 0→1 {{1.0, 0.0}, {1.0, 1.0}}, // s2: same u=1 edge 20); EXPECT_GE(static_cast(level), static_cast(ContinuityLevel::G3)); }