Files
ViewDesignEngine/tests/curves/test_surface_continuity.cpp
T

196 lines
7.6 KiB
C++
Raw Normal View History

#include <gtest/gtest.h>
#include "vde/curves/surface_continuity.h"
#include "vde/curves/nurbs_curve.h"
#include "vde/curves/nurbs_surface.h"
#include <cmath>
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<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 surface from a 2×2 grid
static NurbsSurface simple_surface(const std::vector<std::vector<Point3D>>& grid,
int du=1, int dv=1) {
int rows = static_cast<int>(grid.size());
int cols = static_cast<int>(grid.empty() ? 0 : grid[0].size());
std::vector<double> ku(rows + du + 1, 0.0);
std::vector<double> 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<double>(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<double>(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<std::vector<double>> w(rows, std::vector<double>(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<int>(level), static_cast<int>(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<int>(level), static_cast<int>(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<int>(level), static_cast<int>(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<int>(level), static_cast<int>(ContinuityLevel::G3));
}
TEST(SurfaceContinuityTest, CoplanarSurfacesEdgeMatch) {
// Two adjacent coplanar surfaces sharing an edge
std::vector<std::vector<Point3D>> g1 = {
{Point3D(0,0,0), Point3D(0,1,0)},
{Point3D(1,0,0), Point3D(1,1,0)}
};
std::vector<std::vector<Point3D>> 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<int>(level), static_cast<int>(ContinuityLevel::G3));
}
TEST(SurfaceContinuityTest, DisconnectedSurfaces) {
std::vector<std::vector<Point3D>> g1 = {
{Point3D(0,0,0), Point3D(0,1,0)},
{Point3D(1,0,0), Point3D(1,1,0)}
};
std::vector<std::vector<Point3D>> 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<int>(report.worst), static_cast<int>(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<int>(level), static_cast<int>(ContinuityLevel::G3));
}