test: add degenerate-triangle limiting-angle and edge-DOF guard tests
Finding-F and Finding-G from doc/reviewer/external-audit-2026-05-30.md
(java-port-audit missing-test items 1 and 2).
Finding-F — Degenerate triangle: limiting angles (item 1)
test_euclidean_functional.cpp:
DegenerateTriangle_LimitingAngles_L{12,23,31}TooLong
— verifies α_opposite = π, other two = 0, valid = false
for all three edge-over-long cases
DegenerateTriangle_GradientPicksUpPiCorner
— end-to-end mesh test: forces effective l12 >> l23+l31 via
lambda0, evaluates gradient, asserts G_v3 = π (not 2π from
a skipped degenerate face) and G_v1=G_v2 = 2π
test_spherical_functional.cpp:
DegenerateTriangle_LimitingAngles_S{12,23,31}TooLong
— same coverage for spherical_angles()
Finding-G — euclidean_hessian edge-DOF guard (item 2)
test_euclidean_hessian.cpp:
EdgeDOFGuard_Throws
— assign_euclidean_all_dof_indices + euclidean_hessian → throw
EdgeDOFGuard_VertexOnlyDoesNotThrow
— vertex-only layout → no throw (regression guard)
275/275 CGAL tests pass, 0 failed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -652,3 +652,52 @@ TEST(SphericalGoldenJava, FullMeshEdgeDofGradient_Tetrahedron)
|
||||
EXPECT_NEAR(G[static_cast<std::size_t>(maps.e_idx[eAB])], -0.35189517043413690, 1e-12);
|
||||
EXPECT_NEAR(G[static_cast<std::size_t>(maps.e_idx[eCD])], -0.44101986058895950, 1e-12);
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
// Degenerate spherical triangle — limiting angles (Finding-F, java-port-audit item 1)
|
||||
//
|
||||
// spherical_angles() must return the limiting angles (π opposite the
|
||||
// over-long edge, 0/0 elsewhere) when the spherical triangle inequality
|
||||
// is violated, with valid = false. This mirrors the Euclidean behaviour
|
||||
// and is required for the convex C¹ BPS extension.
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
TEST(SphericalFunctional, DegenerateTriangle_LimitingAngles_S12TooLong)
|
||||
{
|
||||
// s12 > s23 + s31: s12 = 2.5, s23 = s31 = 0.5 (all < π so valid arc lengths)
|
||||
// s23 < 0 → actually use s-based check
|
||||
// Easier: use s12 = π − ε (nearly degenerate hemisphere edge)
|
||||
// and very short s23, s31 so s12 > s23 + s31.
|
||||
const double s12 = 2.0, s23 = 0.4, s31 = 0.4; // s12 > s23+s31 = 0.8
|
||||
auto fa = spherical_angles(s12, s23, s31);
|
||||
EXPECT_FALSE(fa.valid);
|
||||
// s12 is the edge opposite v3 → α3 = π
|
||||
EXPECT_NEAR(fa.alpha3, PI, 1e-12)
|
||||
<< "corner opposite over-long s12 must be π";
|
||||
EXPECT_NEAR(fa.alpha1, 0.0, 1e-12);
|
||||
EXPECT_NEAR(fa.alpha2, 0.0, 1e-12);
|
||||
}
|
||||
|
||||
TEST(SphericalFunctional, DegenerateTriangle_LimitingAngles_S23TooLong)
|
||||
{
|
||||
// s23 > s12 + s31 → α1 = π
|
||||
const double s12 = 0.4, s23 = 2.0, s31 = 0.4;
|
||||
auto fa = spherical_angles(s12, s23, s31);
|
||||
EXPECT_FALSE(fa.valid);
|
||||
EXPECT_NEAR(fa.alpha1, PI, 1e-12)
|
||||
<< "corner opposite over-long s23 must be π";
|
||||
EXPECT_NEAR(fa.alpha2, 0.0, 1e-12);
|
||||
EXPECT_NEAR(fa.alpha3, 0.0, 1e-12);
|
||||
}
|
||||
|
||||
TEST(SphericalFunctional, DegenerateTriangle_LimitingAngles_S31TooLong)
|
||||
{
|
||||
// s31 > s12 + s23 → α2 = π
|
||||
const double s12 = 0.4, s23 = 0.4, s31 = 2.0;
|
||||
auto fa = spherical_angles(s12, s23, s31);
|
||||
EXPECT_FALSE(fa.valid);
|
||||
EXPECT_NEAR(fa.alpha2, PI, 1e-12)
|
||||
<< "corner opposite over-long s31 must be π";
|
||||
EXPECT_NEAR(fa.alpha1, 0.0, 1e-12);
|
||||
EXPECT_NEAR(fa.alpha3, 0.0, 1e-12);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user