fix(v1.0.1): VDE-016 — definitive fix: fully-qualified names throughout
Root cause: GCC two-phase lookup with multiple anonymous namespaces creates barriers that using declarations cannot cross. Fix: Replace all unqualified SSICurveData/ClassResult with vde::brep::SSICurveData/vde::brep::ClassResult throughout the file. This is the only approach verified to work by ViewDesign.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: VDE-016
|
||||
status: open
|
||||
status: fixed
|
||||
severity: critical
|
||||
category: bug
|
||||
date: 2026-07-27
|
||||
|
||||
+21
-21
@@ -64,7 +64,7 @@ namespace {
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Constants & types
|
||||
BrepModel extract_face_fragment(const BrepModel& body, int face_id);
|
||||
ClassResult classify_point_adaptive(
|
||||
vde::brep::ClassResult classify_point_adaptive(
|
||||
const BrepModel& body, const Point3D& p, int& num_exact_upgrades);
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
@@ -507,11 +507,11 @@ BrepModel extract_face_fragment(const BrepModel& body, int face_id) {
|
||||
/// Compute all SSI curves between body A and body B.
|
||||
/// Returns a list of SSICurveData, one per intersecting face pair.
|
||||
/// Uses OpenMP parallelization over face pairs.
|
||||
std::vector<SSICurveData> compute_all_ssi(
|
||||
std::vector<vde::brep::SSICurveData> compute_all_ssi(
|
||||
const BrepModel& a, const BrepModel& b,
|
||||
int& num_exact_upgrades)
|
||||
{
|
||||
std::vector<SSICurveData> all_isects;
|
||||
std::vector<vde::brep::SSICurveData> all_isects;
|
||||
std::mutex isect_mutex;
|
||||
|
||||
// Build face-AABB cache for quick rejection
|
||||
@@ -537,11 +537,11 @@ std::vector<SSICurveData> compute_all_ssi(
|
||||
#ifdef VDE_USE_OPENMP
|
||||
#pragma omp parallel
|
||||
{
|
||||
std::vector<SSICurveData> local;
|
||||
std::vector<vde::brep::SSICurveData> local;
|
||||
#pragma omp for schedule(dynamic) nowait
|
||||
for (size_t pi = 0; pi < pairs.size(); ++pi) {
|
||||
#else
|
||||
std::vector<SSICurveData> local;
|
||||
std::vector<vde::brep::SSICurveData> local;
|
||||
for (size_t pi = 0; pi < pairs.size(); ++pi) {
|
||||
#endif
|
||||
int ia = pairs[pi].first;
|
||||
@@ -558,7 +558,7 @@ std::vector<SSICurveData> compute_all_ssi(
|
||||
for (const auto& curve : curves) {
|
||||
if (curve.points.size() < 4) continue;
|
||||
|
||||
SSICurveData d;
|
||||
vde::brep::SSICurveData d;
|
||||
d.face_a = ia;
|
||||
d.face_b = ib;
|
||||
d.points = curve.points;
|
||||
@@ -610,7 +610,7 @@ struct CuttingPlane {
|
||||
};
|
||||
|
||||
std::unordered_map<int, std::vector<CuttingPlane>>
|
||||
build_face_cutting_planes(const std::vector<SSICurveData>& ssi_curves,
|
||||
build_face_cutting_planes(const std::vector<vde::brep::SSICurveData>& ssi_curves,
|
||||
bool for_body_a) {
|
||||
std::unordered_map<int, std::vector<CuttingPlane>> result;
|
||||
for (const auto& c : ssi_curves) {
|
||||
@@ -641,14 +641,14 @@ build_face_cutting_planes(const std::vector<SSICurveData>& ssi_curves,
|
||||
/// Split all faces of a body using SSI-derived cutting planes,
|
||||
/// then classify each resulting fragment against the other body.
|
||||
/// Returns a vector of (fragment, classification) pairs.
|
||||
std::vector<std::pair<BrepModel, ClassResult>>
|
||||
std::vector<std::pair<BrepModel, vde::brep::ClassResult>>
|
||||
split_and_classify_faces_ssi(
|
||||
const BrepModel& body,
|
||||
const std::unordered_map<int, std::vector<CuttingPlane>>& cutting_planes,
|
||||
const BrepModel& other_body,
|
||||
int& num_exact_upgrades)
|
||||
{
|
||||
std::vector<std::pair<BrepModel, ClassResult>> result;
|
||||
std::vector<std::pair<BrepModel, vde::brep::ClassResult>> result;
|
||||
std::mutex result_mutex;
|
||||
|
||||
#ifdef VDE_USE_OPENMP
|
||||
@@ -685,7 +685,7 @@ split_and_classify_faces_ssi(
|
||||
}
|
||||
|
||||
// Classify each fragment using multi-point sampling
|
||||
std::vector<std::pair<BrepModel, ClassResult>> local;
|
||||
std::vector<std::pair<BrepModel, vde::brep::ClassResult>> local;
|
||||
for (auto& frag : fragments) {
|
||||
// Use the new multi-point classify_face_fragment
|
||||
// Extract face_id from the fragment (fragment has exactly one face at index 0)
|
||||
@@ -711,7 +711,7 @@ split_and_classify_faces_ssi(
|
||||
/// This avoids expensive mesh construction for clearly IN/OUT faces.
|
||||
bool classify_face_uniform(const BrepModel& face_body,
|
||||
const BrepModel& other_body,
|
||||
ClassResult& uniform_result) {
|
||||
vde::brep::ClassResult& uniform_result) {
|
||||
if (face_body.num_faces() == 0) return false;
|
||||
const auto& surf = face_body.surface(face_body.face(0).surface_id);
|
||||
double u0 = surf.knots_u().front(), u1 = surf.knots_u().back();
|
||||
@@ -725,7 +725,7 @@ bool classify_face_uniform(const BrepModel& face_body,
|
||||
double radius = (bb.max() - bb.min()).norm() * 0.5;
|
||||
double radius_sq = radius * radius;
|
||||
|
||||
ClassResult first;
|
||||
vde::brep::ClassResult first;
|
||||
{
|
||||
Point3D p = surf.evaluate(us[0], vs[0]);
|
||||
if (!bb.contains(p)) { first = OUT; }
|
||||
@@ -734,7 +734,7 @@ bool classify_face_uniform(const BrepModel& face_body,
|
||||
}
|
||||
for (int i = 1; i < 5; ++i) {
|
||||
Point3D p = surf.evaluate(us[i], vs[i]);
|
||||
ClassResult cls;
|
||||
vde::brep::ClassResult cls;
|
||||
if (!bb.contains(p)) cls = OUT;
|
||||
else if ((p - center).squaredNorm() <= radius_sq * 0.5) cls = IN;
|
||||
else return false;
|
||||
@@ -747,7 +747,7 @@ bool classify_face_uniform(const BrepModel& face_body,
|
||||
/// Double-precision ray cast classification for a point against a mesh.
|
||||
/// Returns IN/OUT (never ON — ON is handled separately).
|
||||
/// Uses exact_orient3d for robust coplanarity tests on boundary triangles.
|
||||
ClassResult classify_point_double(const BrepModel& body, const Point3D& p) {
|
||||
vde::brep::ClassResult classify_point_double(const BrepModel& body, const Point3D& p) {
|
||||
const mesh::HalfedgeMesh& mesh = get_mesh(body);
|
||||
if (mesh.num_faces() == 0) return OUT;
|
||||
|
||||
@@ -838,7 +838,7 @@ bool point_on_surface_double(const BrepModel& body, const Point3D& p,
|
||||
}
|
||||
|
||||
/// Adaptive classification: try double first, upgrade to GMP exact if uncertain.
|
||||
ClassResult classify_point_adaptive(
|
||||
vde::brep::ClassResult classify_point_adaptive(
|
||||
const BrepModel& body, const Point3D& p, int& num_exact_upgrades)
|
||||
{
|
||||
// Step 1: Check ON surface (double precision is adequate here)
|
||||
@@ -847,12 +847,12 @@ ClassResult classify_point_adaptive(
|
||||
}
|
||||
|
||||
// Step 2: Try double-precision ray cast
|
||||
ClassResult result = classify_point_double(body, p);
|
||||
vde::brep::ClassResult result = classify_point_double(body, p);
|
||||
|
||||
// Step 3: check if the result is uncertain (near boundary)
|
||||
// Re-cast with a slightly offset origin to check consistency
|
||||
Point3D p2(p.x() + 1e-8, p.y(), p.z());
|
||||
ClassResult verify = classify_point_double(body, p2);
|
||||
vde::brep::ClassResult verify = classify_point_double(body, p2);
|
||||
|
||||
if (result != verify) {
|
||||
// Inconsistent: upgrade to exact
|
||||
@@ -947,7 +947,7 @@ ClassResult classify_point_adaptive(
|
||||
|
||||
} // anonymous namespace — classify_face_fragment is public
|
||||
|
||||
ClassResult classify_face_fragment(
|
||||
vde::brep::ClassResult classify_face_fragment(
|
||||
const BrepModel& body, const BrepModel& other_body,
|
||||
int face_id, int& num_exact_upgrades)
|
||||
{
|
||||
@@ -986,7 +986,7 @@ ClassResult classify_face_fragment(
|
||||
std::mt19937 rng(42); // fixed seed for reproducibility
|
||||
std::uniform_real_distribution<double> dist(0.0, 1.0);
|
||||
|
||||
auto sample_and_vote = [&](int n_samples) -> ClassResult {
|
||||
auto sample_and_vote = [&](int n_samples) -> vde::brep::ClassResult {
|
||||
int in_count = 0, out_count = 0, on_count = 0;
|
||||
|
||||
for (int i = 0; i < n_samples; ++i) {
|
||||
@@ -1100,7 +1100,7 @@ ClassResult classify_face_fragment(
|
||||
};
|
||||
|
||||
// First pass: N samples
|
||||
ClassResult result = sample_and_vote(N);
|
||||
vde::brep::ClassResult result = sample_and_vote(N);
|
||||
|
||||
// If result is ON or the vote was close, re-sample with 2N points
|
||||
if (result == ON) {
|
||||
@@ -1377,7 +1377,7 @@ SSIBooleanResult ssi_boolean_core(
|
||||
std::vector<BrepModel> keep_fragments;
|
||||
|
||||
// Predicate: which fragments to keep based on operation type
|
||||
auto should_keep = [op_type](ClassResult cls, bool from_a) -> bool {
|
||||
auto should_keep = [op_type](vde::brep::ClassResult cls, bool from_a) -> bool {
|
||||
switch (op_type) {
|
||||
case 0: // union
|
||||
// Keep A fragments OUT/ON of B; keep B fragments OUT of A
|
||||
|
||||
Reference in New Issue
Block a user