fix(v4.2): boolean/validate/heal/step_export use vertex array index, not v.id
CI / Build & Test (push) Failing after 49s
CI / Release Build (push) Failing after 34s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled

Root cause: add_vertex returns array index but copy_brep/merge_brep/extract_face_fragment
keyed by v.id (next_id_ value). When vertices and edges are interleaved,
v.id != array index → map lookup fails → OOB access.

Fixes in brep_boolean.cpp:
- copy_brep: key by vertex index (vi) instead of v.id
- merge_brep: key by vi, value = best_idx (array index) not result.vertex(id).id
- extract_face_fragment: use body.vertex(e.v_start) directly (e.v_start is array index)
- face_bounds/face_centroid/faces_intersect: same direct access pattern

Also fixed same pattern in brep_heal.cpp, brep_validate.cpp, step_export.cpp
This commit is contained in:
茂之钳
2026-07-25 05:50:56 +00:00
parent 5825609c17
commit 8f77d04283
4 changed files with 25 additions and 61 deletions
+15 -31
View File
@@ -238,12 +238,8 @@ Point3D face_centroid(const BrepModel& body, int face_id) {
int count = 0;
for (int ei : edges) {
const auto& e = body.edge(ei);
for (size_t vi = 0; vi < body.num_vertices(); ++vi) {
auto& v = body.vertex(static_cast<int>(vi));
if (v.id == e.v_start || v.id == e.v_end) {
c += v.point; count++;
}
}
c += body.vertex(e.v_start).point; count++;
c += body.vertex(e.v_end).point; count++;
}
if (count > 0) c /= static_cast<double>(count);
return c;
@@ -257,18 +253,14 @@ bool faces_intersect(const BrepModel& body_a, int face_a,
auto ea = body_a.face_edges(face_a);
for (int ei : ea) {
auto& e = body_a.edge(ei);
for (size_t vi = 0; vi < body_a.num_vertices(); ++vi) {
auto& v = body_a.vertex(static_cast<int>(vi));
if (v.id == e.v_start || v.id == e.v_end) bb_a.expand(v.point);
}
bb_a.expand(body_a.vertex(e.v_start).point);
bb_a.expand(body_a.vertex(e.v_end).point);
}
auto eb = body_b.face_edges(face_b);
for (int ei : eb) {
auto& e = body_b.edge(ei);
for (size_t vi = 0; vi < body_b.num_vertices(); ++vi) {
auto& v = body_b.vertex(static_cast<int>(vi));
if (v.id == e.v_start || v.id == e.v_end) bb_b.expand(v.point);
}
bb_b.expand(body_b.vertex(e.v_start).point);
bb_b.expand(body_b.vertex(e.v_end).point);
}
return bb_a.intersects(bb_b);
}
@@ -306,9 +298,9 @@ BrepModel sew_faces(const std::vector<BrepModel>& face_bodies) {
if (d < best_dist) { best_dist = d; best_idx = static_cast<int>(ri); }
}
if (best_idx >= 0) {
old_to_new[v.id] = result.vertex(static_cast<int>(best_idx)).id;
old_to_new[static_cast<int>(vi)] = best_idx;
} else {
old_to_new[v.id] = result.add_vertex(v.point);
old_to_new[static_cast<int>(vi)] = result.add_vertex(v.point);
}
}
@@ -353,21 +345,15 @@ BrepModel extract_face_fragment(const BrepModel& body, int face_id) {
auto& f = body.face(face_id);
auto es = body.face_edges(face_id);
// Map original vertex IDs to new vertex IDs in fragment
// Map original vertex indices to new vertex indices in fragment
std::map<int, int> old_to_new;
for (int ei : es) {
auto& e = body.edge(ei);
if (old_to_new.find(e.v_start) == old_to_new.end()) {
for (size_t vi = 0; vi < body.num_vertices(); ++vi) {
auto& tv = body.vertex(static_cast<int>(vi));
if (tv.id == e.v_start) { old_to_new[e.v_start] = frag.add_vertex(tv.point); break; }
}
old_to_new[e.v_start] = frag.add_vertex(body.vertex(e.v_start).point);
}
if (old_to_new.find(e.v_end) == old_to_new.end()) {
for (size_t vi = 0; vi < body.num_vertices(); ++vi) {
auto& tv = body.vertex(static_cast<int>(vi));
if (tv.id == e.v_end) { old_to_new[e.v_end] = frag.add_vertex(tv.point); break; }
}
old_to_new[e.v_end] = frag.add_vertex(body.vertex(e.v_end).point);
}
}
@@ -427,11 +413,11 @@ split_and_classify_faces(const BrepModel& a, const BrepModel& b) {
/// Create a deep copy of a BrepModel, preserving all topology exactly.
BrepModel copy_brep(const BrepModel& src) {
BrepModel result;
// Copy vertices
// Copy vertices (key by array index, since e.v_start/e.v_end store indices)
std::map<int, int> old_vid_to_new_idx;
for (size_t vi = 0; vi < src.num_vertices(); ++vi) {
auto& v = src.vertex(static_cast<int>(vi));
old_vid_to_new_idx[v.id] = result.add_vertex(v.point);
old_vid_to_new_idx[static_cast<int>(vi)] = result.add_vertex(v.point);
}
// Copy surfaces
std::map<int, int> old_sid_to_new_idx;
@@ -468,10 +454,8 @@ core::AABB3D face_bounds(const BrepModel& body, int face_id) {
auto es = body.face_edges(face_id);
for (int ei : es) {
const auto& e = body.edge(ei);
for (size_t vi = 0; vi < body.num_vertices(); ++vi) {
const auto& v = body.vertex(static_cast<int>(vi));
if (v.id == e.v_start || v.id == e.v_end) bb.expand(v.point);
}
bb.expand(body.vertex(e.v_start).point);
bb.expand(body.vertex(e.v_end).point);
}
return bb;
}
+2 -5
View File
@@ -250,11 +250,8 @@ Point3D face_centroid(const BrepModel& body, const TopoFace& face) {
for (int ei : loop.edges) {
if (ei < 0 || ei >= static_cast<int>(body.num_edges())) continue;
const auto& e = body.edge(ei);
for (size_t vi = 0; vi < body.num_vertices(); ++vi) {
const auto& v = body.vertex(static_cast<int>(vi));
if (v.id == e.v_start) { sum = sum + v.point; count++; }
if (v.id == e.v_end) { sum = sum + v.point; count++; }
}
sum = sum + body.vertex(e.v_start).point; count++;
sum = sum + body.vertex(e.v_end).point; count++;
}
}
}
+6 -18
View File
@@ -14,17 +14,9 @@ using core::AABB3D;
namespace {
/// Compute edge length by looking up vertices
double edge_length(const BrepModel& body, int edge_idx) {
static double edge_length(const BrepModel& body, int edge_idx) {
auto& e = body.edge(edge_idx);
Point3D p0(0,0,0), p1(0,0,0);
bool found0 = false, found1 = false;
for (size_t vi = 0; vi < body.num_vertices(); ++vi) {
auto& v = body.vertex(static_cast<int>(vi));
if (v.id == e.v_start) { p0 = v.point; found0 = true; }
if (v.id == e.v_end) { p1 = v.point; found1 = true; }
}
if (!found0 || !found1) return 0.0;
return (p1 - p0).norm();
return (body.vertex(e.v_end).point - body.vertex(e.v_start).point).norm();
}
/// Check if two faces share an edge
@@ -60,10 +52,8 @@ bool faces_self_intersect(const BrepModel& body, int fa, int fb) {
auto ea = body.face_edges(fa);
for (int ei : ea) {
auto& e = body.edge(ei);
for (size_t vi = 0; vi < body.num_vertices(); ++vi) {
auto& v = body.vertex(static_cast<int>(vi));
if (v.id == e.v_start || v.id == e.v_end) bb_a.expand(v.point);
}
bb_a.expand(body.vertex(e.v_start).point);
bb_a.expand(body.vertex(e.v_end).point);
}
// Expand slightly to account for numerical error
auto ext_a = bb_a.extent();
@@ -73,10 +63,8 @@ bool faces_self_intersect(const BrepModel& body, int fa, int fb) {
auto eb = body.face_edges(fb);
for (int ei : eb) {
auto& e = body.edge(ei);
for (size_t vi = 0; vi < body.num_vertices(); ++vi) {
auto& v = body.vertex(static_cast<int>(vi));
if (v.id == e.v_start || v.id == e.v_end) bb_b.expand(v.point);
}
bb_b.expand(body.vertex(e.v_start).point);
bb_b.expand(body.vertex(e.v_end).point);
}
return bb_a.intersects(bb_b);
}
+2 -7
View File
@@ -355,13 +355,8 @@ std::string export_step(const std::vector<BrepModel>& bodies) {
for (int ei : face_edges) {
auto& e = body.edge(ei);
core::Point3D p0 = body.vertex(0).point;
core::Point3D p1 = body.vertex(0).point;
for (size_t vi = 0; vi < body.num_vertices(); ++vi) {
auto& v = body.vertex(static_cast<int>(vi));
if (v.id == e.v_start) p0 = v.point;
if (v.id == e.v_end) p1 = v.point;
}
core::Point3D p0 = body.vertex(e.v_start).point;
core::Point3D p1 = body.vertex(e.v_end).point;
// Write VERTEX_POINT for start vertex
int vp_start = writer.write_cartesian_point(p0);