fix(hyper-ideal): guard face_energy() against unsupported multi-ideal faces
Finding-A from doc/reviewer/external-audit-2026-05-30.md. Root cause: both the Java reference (HyperIdealFunctional.java:222-231) and the C++ port silently applied the one-ideal-vertex volume formula to the first ideal vertex found in a face, ignoring any additional ideal vertices. For two or three ideal vertices this produces a wrong energy value with no diagnostic. Fix: add an ideal_count guard at the top of face_energy() that throws std::logic_error for ideal_count >= 2. The one-ideal (Kolpakov-Mednykh) and zero-ideal (Meyerhoff/Ushijima) paths are unchanged and correct. Three new GTests cover the three guard cases: MultiIdealGuard_TwoIdealVertices_Throws (two ideal → throw) MultiIdealGuard_AllThreeIdealVertices_Throws (all ideal → throw) MultiIdealGuard_ExactlyOneIdeal_DoesNotThrow (one ideal → no throw) 262/262 CGAL tests pass, 0 failed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
committed by
Tarik Moussa
parent
7f1a077269
commit
9afefcbb7b
@@ -34,6 +34,7 @@
|
||||
#include "hyper_ideal_geometry.hpp"
|
||||
#include "hyper_ideal_utility.hpp"
|
||||
#include <CGAL/boost/graph/iterator.h>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
@@ -317,25 +318,54 @@ static FaceAngles compute_face_angles(
|
||||
}
|
||||
|
||||
/// Per-face energy contribution U(f) before subtracting the θ·a and Θ·b terms.
|
||||
///
|
||||
/// Supported configurations (faithful port of HyperIdealFunctional.java):
|
||||
/// * All three vertices hyper-ideal (v?b = true) → Meyerhoff/Ushijima volume
|
||||
/// * Exactly one vertex ideal (v?b = false, other two true) → Kolpakov-Mednykh volume
|
||||
///
|
||||
/// NOT supported — faces with two or three ideal vertices. The Java reference
|
||||
/// (HyperIdealFunctional.java lines 222-231) uses an if/else-if chain that
|
||||
/// silently applies the one-ideal-vertex formula to the first ideal vertex it
|
||||
/// finds, ignoring additional ideal vertices. That is mathematically wrong for
|
||||
/// two-ideal or three-ideal faces. Rather than silently computing a wrong result,
|
||||
/// this C++ port throws immediately so the caller can diagnose the problem.
|
||||
/// The correct volume formulas for semi-ideal and fully-ideal faces are not
|
||||
/// implemented in the Java reference and would require new research.
|
||||
static double face_energy(const FaceAngles& fa)
|
||||
{
|
||||
// Guard: reject configurations with 2 or 3 ideal vertices in one face.
|
||||
const int ideal_count = (!fa.v1b ? 1 : 0)
|
||||
+ (!fa.v2b ? 1 : 0)
|
||||
+ (!fa.v3b ? 1 : 0);
|
||||
if (ideal_count >= 2)
|
||||
throw std::logic_error(
|
||||
"face_energy: faces with 2 or 3 ideal (pinned) vertices are not "
|
||||
"supported. Only 0-ideal (all hyper-ideal) and 1-ideal faces are "
|
||||
"implemented, matching the Java HyperIdealFunctional reference. "
|
||||
"Check your v_idx assignments: at most one vertex per face may be "
|
||||
"pinned (v_idx = -1).");
|
||||
|
||||
double aa = fa.a12*fa.alpha12 + fa.a23*fa.alpha23 + fa.a31*fa.alpha31;
|
||||
double bb = fa.b1 *fa.beta1 + fa.b2 *fa.beta2 + fa.b3 *fa.beta3;
|
||||
|
||||
double V = 0.0;
|
||||
if (fa.v1b && fa.v2b && fa.v3b) {
|
||||
// All three vertices are hyper-ideal.
|
||||
V = calculateTetrahedronVolume(
|
||||
fa.beta1, fa.beta2, fa.beta3,
|
||||
fa.alpha23, fa.alpha31, fa.alpha12);
|
||||
} else if (!fa.v1b) {
|
||||
// Exactly v1 is ideal (ideal_count == 1 guaranteed by guard above).
|
||||
V = calculateTetrahedronVolumeWithIdealVertexAtGamma(
|
||||
fa.beta1, fa.alpha31, fa.alpha12,
|
||||
fa.alpha23, fa.beta2, fa.beta3);
|
||||
} else if (!fa.v2b) {
|
||||
// Exactly v2 is ideal.
|
||||
V = calculateTetrahedronVolumeWithIdealVertexAtGamma(
|
||||
fa.beta2, fa.alpha12, fa.alpha23,
|
||||
fa.alpha31, fa.beta3, fa.beta1);
|
||||
} else { // !v3b
|
||||
} else {
|
||||
// Exactly v3 is ideal (!v3b, guaranteed by ideal_count == 1).
|
||||
V = calculateTetrahedronVolumeWithIdealVertexAtGamma(
|
||||
fa.beta3, fa.alpha23, fa.alpha31,
|
||||
fa.alpha12, fa.beta1, fa.beta2);
|
||||
|
||||
Reference in New Issue
Block a user