From 7810d86eeb9517de8a6dfba1358dbd4d706ede5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8C=82=E4=B9=8B=E9=92=B3?= Date: Sat, 25 Jul 2026 06:31:28 +0000 Subject: [PATCH] perf(v4.2): O(1) spatial hash vertex dedup in sew_faces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sew_faces had O(n²) linear scan for vertex deduplication (for each new vertex, scan all existing result vertices). Replaced with quantized position hash map: O(1) lookup per vertex. Box-cylinder union: 92s → 55s (-40%) --- src/brep/brep_boolean.cpp | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/brep/brep_boolean.cpp b/src/brep/brep_boolean.cpp index 6ff0309..f827471 100644 --- a/src/brep/brep_boolean.cpp +++ b/src/brep/brep_boolean.cpp @@ -6,9 +6,11 @@ #include "vde/mesh/halfedge_mesh.h" #include "vde/curves/surface_intersection.h" #include +#include #include #include #include +#include #include #include #include @@ -286,21 +288,33 @@ BrepModel sew_faces(const std::vector& face_bodies) { // Cache: (min_vertex, max_vertex) → edge_id for edge sharing std::map, int> edge_cache; + // Spatial hash for O(1) vertex dedup — quantize positions to 1e-5 + static constexpr double Q = 1e5; // quantization factor + auto quantize = [](double v) -> int64_t { return static_cast(v * Q); }; + std::unordered_map pos_to_idx; + + auto hash_pos = [&](const Point3D& p) -> uint64_t { + uint64_t h = static_cast(quantize(p.x())); + h = h * 0x9e3779b9 + static_cast(quantize(p.y())); + h = h * 0x9e3779b9 + static_cast(quantize(p.z())); + return h; + }; + for (auto& fb : face_bodies) { - // Merge vertices (deduplicate by proximity) - std::map old_to_new; // fragment vertex ID → result vertex index + // Merge vertices (deduplicate by proximity via spatial hash) + std::map old_to_new; + const double tol_sq = 1e-12; // squared tolerance for (size_t vi = 0; vi < fb.num_vertices(); ++vi) { auto& v = fb.vertex(static_cast(vi)); - int best_idx = -1; - double best_dist = 1e-6; - for (size_t ri = 0; ri < result.num_vertices(); ++ri) { - double d = (result.vertex(static_cast(ri)).point - v.point).norm(); - if (d < best_dist) { best_dist = d; best_idx = static_cast(ri); } - } - if (best_idx >= 0) { - old_to_new[static_cast(vi)] = best_idx; + uint64_t h = hash_pos(v.point); + auto it = pos_to_idx.find(h); + if (it != pos_to_idx.end() && + (result.vertex(it->second).point - v.point).squaredNorm() < tol_sq) { + old_to_new[static_cast(vi)] = it->second; } else { - old_to_new[static_cast(vi)] = result.add_vertex(v.point); + int new_idx = result.add_vertex(v.point); + old_to_new[static_cast(vi)] = new_idx; + pos_to_idx[h] = new_idx; } }