From 2196175dfc502f02b04f31485ce9da892b1255c5 Mon Sep 17 00:00:00 2001 From: Seth Parker Date: Sun, 14 Jun 2026 21:57:51 -0400 Subject: [PATCH 1/2] refactor: align identifier naming with codebase convention (#94) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply the codebase naming convention (codified in conductor/code_styleguides/cpp.md, commit 26fa1fb) to existing camelCase identifiers across the LSCM/HLSCM and ABF surfaces: Group A — detail:: namespace (no public API impact, no shims): - detail::lscm::buildSystem -> BuildSystem - detail::hlscm::buildHierarchy -> BuildHierarchy - detail::hlscm::buildLevelMesh -> BuildLevelMesh - detail::hlscm::prolongateUVs -> ProlongateUVs - detail::hlscm::solveLSCMLevel -> SolveLSCMLevel - DecimationMesh::numAliveVerts -> num_alive_verts - DecimationMesh::numAliveFaces -> num_alive_faces - DecimationMesh::tryCollapse -> try_collapse - DecimationMesh::collapseCost -> collapse_cost - DecimationMesh::isAlive -> is_alive - DecimationMesh::isCollapsible -> is_collapsible - DecimationMesh::vertexNeighbors -> vertex_neighbors - DecimationMesh::rebuildAndGetEdges -> rebuild_and_get_edges - DecimationMesh private methods -> compute_quadrics_, build_edges_ - DecimationMesh private members -> is_boundary_, is_pinned_, face_alive_, vert_faces_, num_alive_verts_, num_alive_faces_, scratch_*_ - CollapseRecord members -> v_removed, v_kept, containing_tri - HierarchyLevel members -> local_to_original, original_to_local Group B — public instance setters (with [[deprecated]] shims slated for removal in 3.0): - ABF/ABFPlusPlus::setMaxIterations -> set_max_iterations - ABF/ABFPlusPlus::setGradientThreshold -> set_gradient_threshold - HierarchicalLSCM::setLevelRatio -> set_level_ratio - HierarchicalLSCM::setMinCoarseVertices -> set_min_coarse_vertices - ABF/ABFPlusPlus::maxIters_ -> max_iters_ - ABF/ABFPlusPlus::gradThreshold_ -> grad_threshold_ - HierarchicalLSCM::levelRatio_ -> level_ratio_ - HierarchicalLSCM::minCoarseVertices_ -> min_coarse_vertices_ - ABLSCM/HLSCM::legacyPinIndices_ -> legacy_pin_indices_ (the latter was a violation I introduced in PR #93) The existing setPinnedVertices deprecated shims (from PR #93) keep their pre-existing camelCase name — they're already slated for 3.0 removal and renaming would double-migrate users. Tests: all 60 parameterization assertions pass (6/6 binaries). Deprecation warnings only fire from the pre-existing PR #93 shim tests; new shims compile clean since test callers were updated. Single-header amalgamation regenerated. --- include/OpenABF/ABF.hpp | 24 +- include/OpenABF/ABFPlusPlus.hpp | 24 +- include/OpenABF/AngleBasedLSCM.hpp | 16 +- include/OpenABF/HierarchicalLSCM.hpp | 299 +++++++++++---------- include/OpenABF/detail/LSCMSystem.hpp | 8 +- single_include/OpenABF/OpenABF.hpp | 359 ++++++++++++++------------ tests/src/TestParameterization.cpp | 197 +++++++------- 7 files changed, 507 insertions(+), 420 deletions(-) diff --git a/include/OpenABF/ABF.hpp b/include/OpenABF/ABF.hpp index c9bd236..98f5e27 100644 --- a/include/OpenABF/ABF.hpp +++ b/include/OpenABF/ABF.hpp @@ -235,10 +235,24 @@ class ABF using Mesh = MeshType; /** @brief Set the maximum number of iterations */ - void setMaxIterations(const std::size_t it) { maxIters_ = it; } + void set_max_iterations(const std::size_t it) { max_iters_ = it; } /** @brief Set the gradient convergence threshold */ - void setGradientThreshold(T t) { gradThreshold_ = t; } + void set_gradient_threshold(T t) { grad_threshold_ = t; } + + /** @deprecated Use `set_max_iterations`; will be removed in 3.0. */ + [[deprecated("Use set_max_iterations; will be removed in 3.0")]] void setMaxIterations( + const std::size_t it) + { + set_max_iterations(it); + } + + /** @deprecated Use `set_gradient_threshold`; will be removed in 3.0. */ + [[deprecated("Use set_gradient_threshold; will be removed in 3.0")]] void setGradientThreshold( + T t) + { + set_gradient_threshold(t); + } /** * @brief Get the mesh gradient @@ -257,7 +271,7 @@ class ABF /** @copydoc ABF::Compute */ void compute(typename Mesh::Pointer& mesh) { - Compute(mesh, iters_, grad_, maxIters_, gradThreshold_); + Compute(mesh, iters_, grad_, max_iters_, grad_threshold_); } /** @@ -436,9 +450,9 @@ class ABF /** Number of executed iterations */ std::size_t iters_{0}; /** Max iterations */ - std::size_t maxIters_{10}; + std::size_t max_iters_{10}; /** Gradient convergence threshold */ - T gradThreshold_{0.001}; + T grad_threshold_{0.001}; }; } // namespace OpenABF \ No newline at end of file diff --git a/include/OpenABF/ABFPlusPlus.hpp b/include/OpenABF/ABFPlusPlus.hpp index 66cd39a..6999d9a 100644 --- a/include/OpenABF/ABFPlusPlus.hpp +++ b/include/OpenABF/ABFPlusPlus.hpp @@ -48,10 +48,24 @@ class ABFPlusPlus using Mesh = MeshType; /** @brief Set the maximum number of iterations */ - void setMaxIterations(std::size_t it) { maxIters_ = it; } + void set_max_iterations(std::size_t it) { max_iters_ = it; } /** @brief Set the gradient convergence threshold */ - void setGradientThreshold(T t) { gradThreshold_ = t; } + void set_gradient_threshold(T t) { grad_threshold_ = t; } + + /** @deprecated Use `set_max_iterations`; will be removed in 3.0. */ + [[deprecated("Use set_max_iterations; will be removed in 3.0")]] void setMaxIterations( + std::size_t it) + { + set_max_iterations(it); + } + + /** @deprecated Use `set_gradient_threshold`; will be removed in 3.0. */ + [[deprecated("Use set_gradient_threshold; will be removed in 3.0")]] void setGradientThreshold( + T t) + { + set_gradient_threshold(t); + } /** * @brief Get the mesh gradient @@ -70,7 +84,7 @@ class ABFPlusPlus /** @copydoc ABFPlusPlus::Compute */ void compute(typename Mesh::Pointer& mesh) { - Compute(mesh, iters_, grad_, maxIters_, gradThreshold_); + Compute(mesh, iters_, grad_, max_iters_, grad_threshold_); } /** @@ -273,9 +287,9 @@ class ABFPlusPlus /** Number of executed iterations */ std::size_t iters_{0}; /** Max iterations */ - std::size_t maxIters_{10}; + std::size_t max_iters_{10}; /** Gradient convergence threshold */ - T gradThreshold_{0.001}; + T grad_threshold_{0.001}; }; } // namespace OpenABF \ No newline at end of file diff --git a/include/OpenABF/AngleBasedLSCM.hpp b/include/OpenABF/AngleBasedLSCM.hpp index ca5a0d2..1ce626a 100644 --- a/include/OpenABF/AngleBasedLSCM.hpp +++ b/include/OpenABF/AngleBasedLSCM.hpp @@ -144,7 +144,7 @@ class AngleBasedLSCM void set_pins(PinMap pins) { pins_ = std::move(pins); - legacyPinIndices_.reset(); + legacy_pin_indices_.reset(); } /** @@ -157,7 +157,7 @@ class AngleBasedLSCM [[deprecated("Use set_pins(PinMap); will be removed in 3.0")]] void setPinnedVertices( std::size_t pin0Idx, std::size_t pin1Idx) { - legacyPinIndices_ = {pin0Idx, pin1Idx}; + legacy_pin_indices_ = {pin0Idx, pin1Idx}; pins_.reset(); } @@ -166,9 +166,9 @@ class AngleBasedLSCM { if (pins_) { Compute(mesh, *pins_); - } else if (legacyPinIndices_) { - ComputeImpl(mesh, detail::lscm::AutoPlacePair(mesh, legacyPinIndices_->first, - legacyPinIndices_->second)); + } else if (legacy_pin_indices_) { + ComputeImpl(mesh, detail::lscm::AutoPlacePair(mesh, legacy_pin_indices_->first, + legacy_pin_indices_->second)); } else { Compute(mesh); } @@ -229,7 +229,7 @@ class AngleBasedLSCM /** Optional explicit pin set configured via `set_pins()`. */ std::optional pins_; /** Deprecated: legacy two-pin index pair set via `setPinnedVertices`. */ - std::optional> legacyPinIndices_; + std::optional> legacy_pin_indices_; /** * @brief Core solver: build the LSCM system from the PinMap, solve for free @@ -240,9 +240,9 @@ class AngleBasedLSCM using SparseMatrix = Eigen::SparseMatrix; using DenseMatrix = Eigen::Matrix; - // LSCM system assembly (shared with HierarchicalLSCM). buildSystem + // LSCM system assembly (shared with HierarchicalLSCM). BuildSystem // does not mutate the mesh; pin UVs are written below. - auto parts = detail::lscm::buildSystem(mesh, pins); + auto parts = detail::lscm::BuildSystem(mesh, pins); // Solve for x auto x = detail::SolveLeastSquares(parts.A, parts.b); diff --git a/include/OpenABF/HierarchicalLSCM.hpp b/include/OpenABF/HierarchicalLSCM.hpp index 4d3627c..b4f6dff 100644 --- a/include/OpenABF/HierarchicalLSCM.hpp +++ b/include/OpenABF/HierarchicalLSCM.hpp @@ -77,12 +77,12 @@ struct Quadric { template struct CollapseRecord { /** Index of the removed vertex (in the original/fine mesh) */ - std::size_t vRemoved; + std::size_t v_removed; /** Index of the kept vertex (in the original/fine mesh) */ - std::size_t vKept; - /** Post-collapse triangle containing vRemoved (original vertex indices) */ - std::array containingTri; - /** Barycentric coordinates of vRemoved in containingTri */ + std::size_t v_kept; + /** Post-collapse triangle containing v_removed (original vertex indices) */ + std::array containing_tri; + /** Barycentric coordinates of v_removed in containing_tri */ std::array bary; }; @@ -96,12 +96,12 @@ struct HierarchyLevel { /** Face connectivity (each face is 3 level-local indices) */ std::vector> faces; /** Map from level-local vertex index to original (finest) vertex index */ - std::vector localToOriginal; + std::vector local_to_original; /** Map from original vertex index to level-local index. Sized to the * finest-mesh vertex count; vertices not present at this level hold * `std::nullopt`. */ - std::vector> originalToLocal; + std::vector> original_to_local; }; /** @@ -123,22 +123,22 @@ class DecimationMesh positions_.resize(nv); alive_.assign(nv, true); - isBoundary_.assign(nv, false); - isPinned_.assign(nv, false); + is_boundary_.assign(nv, false); + is_pinned_.assign(nv, false); quadrics_.resize(nv); for (auto idx : pinIndices) { - isPinned_[idx] = true; + is_pinned_[idx] = true; } for (const auto& v : mesh->vertices()) { positions_[v->idx] = v->pos; - isBoundary_[v->idx] = v->is_boundary(); + is_boundary_[v->idx] = v->is_boundary(); } faces_.reserve(nf); - faceAlive_.reserve(nf); - vertFaces_.resize(nv); + face_alive_.reserve(nf); + vert_faces_.resize(nv); for (const auto& f : mesh->faces()) { auto e0 = f->head; @@ -147,24 +147,24 @@ class DecimationMesh std::array tri{e0->vertex->idx, e1->vertex->idx, e2->vertex->idx}; auto fi = faces_.size(); faces_.push_back(tri); - faceAlive_.push_back(true); - vertFaces_[tri[0]].push_back(fi); - vertFaces_[tri[1]].push_back(fi); - vertFaces_[tri[2]].push_back(fi); + face_alive_.push_back(true); + vert_faces_[tri[0]].push_back(fi); + vert_faces_[tri[1]].push_back(fi); + vert_faces_[tri[2]].push_back(fi); } - numAliveVerts_ = nv; - numAliveFaces_ = nf; + num_alive_verts_ = nv; + num_alive_faces_ = nf; - computeQuadrics_(); - buildEdges_(); + compute_quadrics_(); + build_edges_(); } /** Get number of alive vertices */ - [[nodiscard]] auto numAliveVerts() const -> std::size_t { return numAliveVerts_; } + [[nodiscard]] auto num_alive_verts() const -> std::size_t { return num_alive_verts_; } /** Get number of alive faces */ - [[nodiscard]] auto numAliveFaces() const -> std::size_t { return numAliveFaces_; } + [[nodiscard]] auto num_alive_faces() const -> std::size_t { return num_alive_faces_; } /** * @brief Try to collapse edge (vRemove → vKeep), returning a collapse record @@ -176,29 +176,29 @@ class DecimationMesh * vertex indices (sorted, unique). Passing a caller- * owned vector here lets the PQ-update path reuse the * allocation across collapses instead of letting - * `vertexNeighbors()` allocate a fresh vector each + * `vertex_neighbors()` allocate a fresh vector each * time. */ - auto tryCollapse(std::size_t vRemove, std::size_t vKeep, - std::vector* outKeepNbrs = nullptr) + auto try_collapse(std::size_t vRemove, std::size_t vKeep, + std::vector* outKeepNbrs = nullptr) -> std::optional> { if (!alive_[vRemove] || !alive_[vKeep]) { return std::nullopt; } - if (isPinned_[vRemove]) { + if (is_pinned_[vRemove]) { return std::nullopt; } // Find shared faces (will be removed) and vRemove-only faces (will be updated) // Reuse scratch storage - auto& sharedFaces = scratchShared_; - auto& removeFaces = scratchRemove_; + auto& sharedFaces = scratch_shared_; + auto& removeFaces = scratch_remove_; sharedFaces.clear(); removeFaces.clear(); - for (auto fi : vertFaces_[vRemove]) { - if (!faceAlive_[fi]) + for (auto fi : vert_faces_[vRemove]) { + if (!face_alive_[fi]) continue; bool hasKeep = false; for (auto vi : faces_[fi]) { @@ -219,15 +219,15 @@ class DecimationMesh // Reject collapse of two boundary vertices via an interior edge: // vKeep would inherit two disconnected boundary fans → non-manifold. - if (isBoundary_[vRemove] && isBoundary_[vKeep] && sharedFaces.size() == 2) { + if (is_boundary_[vRemove] && is_boundary_[vKeep] && sharedFaces.size() == 2) { return std::nullopt; } // Link condition: collect sorted unique neighbors of vRemove and vKeep auto fillSortedNeighbors = [&](std::size_t v, std::vector& out) { out.clear(); - for (auto fi : vertFaces_[v]) { - if (!faceAlive_[fi]) + for (auto fi : vert_faces_[v]) { + if (!face_alive_[fi]) continue; for (auto vi : faces_[fi]) { if (vi != v) @@ -238,17 +238,18 @@ class DecimationMesh out.erase(std::unique(out.begin(), out.end()), out.end()); }; - fillSortedNeighbors(vRemove, scratchNbrsA_); - fillSortedNeighbors(vKeep, scratchNbrsB_); + fillSortedNeighbors(vRemove, scratch_nbrs_a_); + fillSortedNeighbors(vKeep, scratch_nbrs_b_); // Shared neighbors (excluding vKeep/vRemove from each other's sets) - scratchSharedNbrs_.clear(); - for (auto v : scratchNbrsA_) { - if (v != vKeep && std::binary_search(scratchNbrsB_.begin(), scratchNbrsB_.end(), v)) { - scratchSharedNbrs_.push_back(v); + scratch_shared_nbrs_.clear(); + for (auto v : scratch_nbrs_a_) { + if (v != vKeep && + std::binary_search(scratch_nbrs_b_.begin(), scratch_nbrs_b_.end(), v)) { + scratch_shared_nbrs_.push_back(v); } } - // scratchSharedNbrs_ is already sorted since scratchNbrsA_ is sorted + // scratch_shared_nbrs_ is already sorted since scratch_nbrs_a_ is sorted // Expected shared: opposite vertices of the shared faces (at most 2 entries) std::array expectedShared{}; @@ -262,10 +263,10 @@ class DecimationMesh if (numExpected > 1 && expectedShared[0] > expectedShared[1]) std::swap(expectedShared[0], expectedShared[1]); - if (scratchSharedNbrs_.size() != numExpected) + if (scratch_shared_nbrs_.size() != numExpected) return std::nullopt; for (std::size_t i = 0; i < numExpected; ++i) { - if (scratchSharedNbrs_[i] != expectedShared[i]) + if (scratch_shared_nbrs_[i] != expectedShared[i]) return std::nullopt; } @@ -279,14 +280,14 @@ class DecimationMesh // maintain a minimum angle above the threshold. // // Collect the full set of post-collapse faces incident to vKeep. - auto& postFaces = scratchPostFaces_; + auto& postFaces = scratch_post_faces_; postFaces.clear(); // Existing vKeep faces (excluding shared faces which will be removed) auto isInShared = [&](std::size_t fi) { return std::find(sharedFaces.begin(), sharedFaces.end(), fi) != sharedFaces.end(); }; - for (auto fi : vertFaces_[vKeep]) { - if (!faceAlive_[fi] || isInShared(fi)) { + for (auto fi : vert_faces_[vKeep]) { + if (!face_alive_[fi] || isInShared(fi)) { continue; } postFaces.push_back(faces_[fi]); @@ -345,10 +346,10 @@ class DecimationMesh // Build collapse record with barycentric coordinates // After collapse, face (vRemove, vA, vB) becomes (vKeep, vA, vB). - // Store bary coords of vRemoved's position in the post-collapse triangle. + // Store bary coords of v_removed's position in the post-collapse triangle. CollapseRecord record; - record.vRemoved = vRemove; - record.vKept = vKeep; + record.v_removed = vRemove; + record.v_kept = vKeep; if (!removeFaces.empty()) { // Use the first surviving face; its post-collapse vertices are @@ -362,20 +363,20 @@ class DecimationMesh postTri[slot++] = vi; } } - record.containingTri = postTri; + record.containing_tri = postTri; record.bary = computeBarycentric_(positions_[postTri[0]], positions_[postTri[1]], positions_[postTri[2]], positions_[vRemove]); } else { // Edge case: all faces are shared — vertex collapses directly onto vKeep - record.containingTri = {vKeep, vKeep, vKeep}; + record.containing_tri = {vKeep, vKeep, vKeep}; record.bary = {T(1), T(0), T(0)}; } // Execute collapse // Kill shared faces for (auto fi : sharedFaces) { - faceAlive_[fi] = false; - numAliveFaces_--; + face_alive_[fi] = false; + num_alive_faces_--; } // Update removeFaces: replace vRemove with vKeep @@ -385,30 +386,30 @@ class DecimationMesh vi = vKeep; } } - vertFaces_[vKeep].push_back(fi); + vert_faces_[vKeep].push_back(fi); } // Mark vRemove as dead alive_[vRemove] = false; - numAliveVerts_--; + num_alive_verts_--; // Propagate boundary status: if vRemove was on the boundary, // vKeep inherits it (it now sits on the mesh boundary). - if (isBoundary_[vRemove]) { - isBoundary_[vKeep] = true; + if (is_boundary_[vRemove]) { + is_boundary_[vKeep] = true; } // Merge quadrics quadrics_[vKeep] += quadrics_[vRemove]; // Compact dead face indices from vKeep's adjacency list - auto& vkFaces = vertFaces_[vKeep]; + auto& vkFaces = vert_faces_[vKeep]; vkFaces.erase(std::remove_if(vkFaces.begin(), vkFaces.end(), - [this](std::size_t fi) { return !faceAlive_[fi]; }), + [this](std::size_t fi) { return !face_alive_[fi]; }), vkFaces.end()); // Fill caller-owned post-collapse neighbor vector (reusing its - // allocation across calls). Equivalent to vertexNeighbors(vKeep) but + // allocation across calls). Equivalent to vertex_neighbors(vKeep) but // without an additional heap allocation. if (outKeepNbrs != nullptr) { outKeepNbrs->clear(); @@ -428,7 +429,7 @@ class DecimationMesh } /** Compute collapse cost for edge (v0 → v1): Q_merged evaluated at v1 */ - [[nodiscard]] auto collapseCost(std::size_t v0, std::size_t v1) const -> T + [[nodiscard]] auto collapse_cost(std::size_t v0, std::size_t v1) const -> T { auto Q = quadrics_[v0] + quadrics_[v1]; auto& p = positions_[v1]; @@ -436,25 +437,25 @@ class DecimationMesh } /** Check if a vertex is alive */ - [[nodiscard]] auto isAlive(std::size_t v) const -> bool { return alive_[v]; } + [[nodiscard]] auto is_alive(std::size_t v) const -> bool { return alive_[v]; } /** Check if a vertex is collapsible (not pinned, alive) */ - [[nodiscard]] auto isCollapsible(std::size_t v) const -> bool + [[nodiscard]] auto is_collapsible(std::size_t v) const -> bool { - return alive_[v] && !isPinned_[v]; + return alive_[v] && !is_pinned_[v]; } /** Get alive neighbour vertices of `v` (sorted, deduped). * - * Production code path uses `tryCollapse`'s `outKeepNbrs` out-param to + * Production code path uses `try_collapse`'s `outKeepNbrs` out-param to * avoid this method's per-call allocation; this overload is retained * only as the oracle for the `HLSCMInternal.TryCollapse_*` tests. */ - [[nodiscard]] auto vertexNeighbors(std::size_t v) const -> std::vector + [[nodiscard]] auto vertex_neighbors(std::size_t v) const -> std::vector { std::vector nbrs; - for (auto fi : vertFaces_[v]) { - if (!faceAlive_[fi]) + for (auto fi : vert_faces_[v]) { + if (!face_alive_[fi]) continue; for (auto vi : faces_[fi]) { if (vi != v && alive_[vi]) @@ -470,14 +471,14 @@ class DecimationMesh [[nodiscard]] auto snapshot() const -> HierarchyLevel { HierarchyLevel level; - level.originalToLocal.assign(alive_.size(), std::nullopt); + level.original_to_local.assign(alive_.size(), std::nullopt); // Build mapping from original indices to level-local indices std::size_t localIdx = 0; for (std::size_t i = 0; i < alive_.size(); ++i) { if (alive_[i]) { - level.originalToLocal[i] = localIdx; - level.localToOriginal.push_back(i); + level.original_to_local[i] = localIdx; + level.local_to_original.push_back(i); level.positions.push_back(positions_[i]); localIdx++; } @@ -486,12 +487,12 @@ class DecimationMesh // Remap faces. Every alive face references only alive vertices, so // the unwrap is always safe here. for (std::size_t fi = 0; fi < faces_.size(); ++fi) { - if (!faceAlive_[fi]) { + if (!face_alive_[fi]) { continue; } std::array localTri; for (int j = 0; j < 3; ++j) { - localTri[j] = *level.originalToLocal[faces_[fi][j]]; + localTri[j] = *level.original_to_local[faces_[fi][j]]; } level.faces.push_back(localTri); } @@ -500,22 +501,22 @@ class DecimationMesh } /** Rebuild the edge list from alive faces and return it */ - auto rebuildAndGetEdges() -> const std::vector>& + auto rebuild_and_get_edges() -> const std::vector>& { - buildEdges_(); + build_edges_(); return edges_; } private: /** Recompute per-vertex QEM quadrics from the live faces (Garland-Heckbert). */ - void computeQuadrics_() + void compute_quadrics_() { for (auto& q : quadrics_) { q = Quadric(); } for (std::size_t fi = 0; fi < faces_.size(); ++fi) { - if (!faceAlive_[fi]) { + if (!face_alive_[fi]) { continue; } auto& tri = faces_[fi]; @@ -544,7 +545,7 @@ class DecimationMesh } /** Rebuild the unique-edge list from the live faces. */ - void buildEdges_() + void build_edges_() { edges_.clear(); std::unordered_set seen; @@ -555,7 +556,7 @@ class DecimationMesh }; for (std::size_t fi = 0; fi < faces_.size(); ++fi) { - if (!faceAlive_[fi]) { + if (!face_alive_[fi]) { continue; } auto& tri = faces_[fi]; @@ -605,36 +606,36 @@ class DecimationMesh /** Per-vertex alive flag; collapses set entries to false. */ std::vector alive_; /** Per-vertex boundary flag, copied from the source mesh. */ - std::vector isBoundary_; + std::vector is_boundary_; /** Per-vertex pinned flag; pinned vertices cannot be removed by collapse. */ - std::vector isPinned_; + std::vector is_pinned_; /** Per-vertex QEM quadrics accumulated from incident face planes. */ std::vector> quadrics_; /** Face connectivity: each face is three vertex indices into `positions_`. */ std::vector> faces_; /** Per-face alive flag; collapses retire faces by setting entries to false. */ - std::vector faceAlive_; + std::vector face_alive_; /** Per-vertex incident face list. */ - std::vector> vertFaces_; + std::vector> vert_faces_; /** Count of alive vertices (kept current across collapses). */ - std::size_t numAliveVerts_{0}; + std::size_t num_alive_verts_{0}; /** Count of alive faces (kept current across collapses). */ - std::size_t numAliveFaces_{0}; - /** Unique-edge list, rebuilt on demand by `buildEdges_()`. */ + std::size_t num_alive_faces_{0}; + /** Unique-edge list, rebuilt on demand by `build_edges_()`. */ std::vector> edges_; /** Scratch buffer: neighbor indices shared between two endpoints. */ - std::vector scratchShared_; + std::vector scratch_shared_; /** Scratch buffer: faces to retire on a collapse. */ - std::vector scratchRemove_; + std::vector scratch_remove_; /** Scratch buffer: post-collapse face rewrites. */ - std::vector> scratchPostFaces_; + std::vector> scratch_post_faces_; /** Scratch buffer: neighbor list of one endpoint. */ - std::vector scratchNbrsA_; + std::vector scratch_nbrs_a_; /** Scratch buffer: neighbor list of the other endpoint. */ - std::vector scratchNbrsB_; - /** Scratch buffer: deduplicated union of `scratchNbrsA_` and `scratchNbrsB_`. */ - std::vector scratchSharedNbrs_; + std::vector scratch_nbrs_b_; + /** Scratch buffer: deduplicated union of `scratch_nbrs_a_` and `scratch_nbrs_b_`. */ + std::vector scratch_shared_nbrs_; }; /** @@ -644,7 +645,7 @@ class DecimationMesh * the collapse records needed for prolongation (ordered from finest to coarsest). */ template -auto buildHierarchy(const MeshPtr& mesh, const std::vector& pinIndices, +auto BuildHierarchy(const MeshPtr& mesh, const std::vector& pinIndices, std::size_t levelRatio, std::size_t minCoarseVerts) -> std::pair>, std::vector>>> { @@ -657,14 +658,14 @@ auto buildHierarchy(const MeshPtr& mesh, const std::vector& pinIndi std::vector>> collapsesByLevel; - auto targetVerts = dmesh.numAliveVerts(); + auto targetVerts = dmesh.num_alive_verts(); if (targetVerts <= minCoarseVerts) { // Mesh is already small enough — single level return {levels, collapsesByLevel}; } // Reused across collapses to avoid per-collapse heap allocation; populated - // by tryCollapse with vKeep's post-collapse neighbors. + // by try_collapse with vKeep's post-collapse neighbors. std::vector nbrs; while (targetVerts > minCoarseVerts) { @@ -675,27 +676,27 @@ auto buildHierarchy(const MeshPtr& mesh, const std::vector& pinIndi using CostEdge = std::pair>; std::priority_queue, std::greater> pq; - const auto& edges = dmesh.rebuildAndGetEdges(); + const auto& edges = dmesh.rebuild_and_get_edges(); for (auto& [a, b] : edges) { // Try collapsing the collapsible vertex towards the other - if (dmesh.isCollapsible(a)) { - pq.push({dmesh.collapseCost(a, b), {a, b}}); + if (dmesh.is_collapsible(a)) { + pq.push({dmesh.collapse_cost(a, b), {a, b}}); } - if (dmesh.isCollapsible(b)) { - pq.push({dmesh.collapseCost(b, a), {b, a}}); + if (dmesh.is_collapsible(b)) { + pq.push({dmesh.collapse_cost(b, a), {b, a}}); } } - while (dmesh.numAliveVerts() > nextTarget && !pq.empty()) { + while (dmesh.num_alive_verts() > nextTarget && !pq.empty()) { auto [cost, edge] = pq.top(); pq.pop(); auto [vRemove, vKeep] = edge; // Skip stale entries whose endpoints have already been collapsed - if (!dmesh.isAlive(vRemove) || !dmesh.isAlive(vKeep)) { + if (!dmesh.is_alive(vRemove) || !dmesh.is_alive(vKeep)) { continue; } - auto record = dmesh.tryCollapse(vRemove, vKeep, &nbrs); + auto record = dmesh.try_collapse(vRemove, vKeep, &nbrs); if (!record) { continue; } @@ -703,13 +704,13 @@ auto buildHierarchy(const MeshPtr& mesh, const std::vector& pinIndi levelCollapses.push_back(*record); // Add new edges involving vKeep to the priority queue using the - // post-collapse neighbor list populated by tryCollapse. + // post-collapse neighbor list populated by try_collapse. for (auto nb : nbrs) { - if (dmesh.isCollapsible(nb)) { - pq.push({dmesh.collapseCost(nb, vKeep), {nb, vKeep}}); + if (dmesh.is_collapsible(nb)) { + pq.push({dmesh.collapse_cost(nb, vKeep), {nb, vKeep}}); } - if (dmesh.isCollapsible(vKeep)) { - pq.push({dmesh.collapseCost(vKeep, nb), {vKeep, nb}}); + if (dmesh.is_collapsible(vKeep)) { + pq.push({dmesh.collapse_cost(vKeep, nb), {vKeep, nb}}); } } } @@ -720,7 +721,7 @@ auto buildHierarchy(const MeshPtr& mesh, const std::vector& pinIndi collapsesByLevel.push_back(std::move(levelCollapses)); levels.push_back(dmesh.snapshot()); - targetVerts = dmesh.numAliveVerts(); + targetVerts = dmesh.num_alive_verts(); } return {levels, collapsesByLevel}; @@ -730,7 +731,7 @@ auto buildHierarchy(const MeshPtr& mesh, const std::vector& pinIndi * @brief Build a HalfEdgeMesh from a hierarchy level */ template -auto buildLevelMesh(const HierarchyLevel& level) -> typename HalfEdgeMesh::Pointer +auto BuildLevelMesh(const HierarchyLevel& level) -> typename HalfEdgeMesh::Pointer { auto mesh = HalfEdgeMesh::New(); @@ -765,7 +766,7 @@ using UVVector = std::vector>>; * @param collapses Collapse records for this level transition. */ template -auto prolongateUVs(UVVector uvs, const std::vector>& collapses) -> UVVector +auto ProlongateUVs(UVVector uvs, const std::vector>& collapses) -> UVVector { // Undo collapses in reverse order — the last collapse applied is the first // we need to undo to recover the next-finer level's UVs. All three @@ -773,9 +774,9 @@ auto prolongateUVs(UVVector uvs, const std::vector>& collap // dereference them: they survived the collapse we're undoing. for (auto it = collapses.rbegin(); it != collapses.rend(); ++it) { auto& rec = *it; - auto& tri = rec.containingTri; + auto& tri = rec.containing_tri; - uvs[rec.vRemoved] = + uvs[rec.v_removed] = *uvs[tri[0]] * rec.bary[0] + *uvs[tri[1]] * rec.bary[1] + *uvs[tri[2]] * rec.bary[2]; } @@ -794,7 +795,7 @@ auto prolongateUVs(UVVector uvs, const std::vector>& collap * not present at this level remain `std::nullopt`. */ template -auto solveLSCMLevel(const typename HalfEdgeMesh::Pointer& levelMesh, +auto SolveLSCMLevel(const typename HalfEdgeMesh::Pointer& levelMesh, const detail::hlscm::HierarchyLevel& level, const detail::lscm::PinMap& origPins, std::size_t origVertCount, const UVVector* initialGuess) -> UVVector @@ -808,7 +809,7 @@ auto solveLSCMLevel(const typename HalfEdgeMesh::Pointer& levelMesh, auto numFree = numVerts - numFixed; // Map each original pin idx to its level-local idx. Pins are guaranteed - // to survive every decimation level (DecimationMesh::tryCollapse rejects + // to survive every decimation level (DecimationMesh::try_collapse rejects // collapses of pinned vertices), so this unwrap is expected to succeed. // Throw a meaningful exception rather than std::bad_optional_access if a // future refactor ever breaks the invariant. @@ -817,7 +818,7 @@ auto solveLSCMLevel(const typename HalfEdgeMesh::Pointer& levelMesh, std::unordered_set localPinIdx; localPinIdx.reserve(numFixed); for (const auto& [origIdx, uv] : origPins) { - const auto& localOpt = level.originalToLocal[origIdx]; + const auto& localOpt = level.original_to_local[origIdx]; if (!localOpt.has_value()) { throw SolverException( "HLSCM: pinned vertex was removed during decimation (invariant violated)"); @@ -828,7 +829,7 @@ auto solveLSCMLevel(const typename HalfEdgeMesh::Pointer& levelMesh, } // Pin placement + LSCM system assembly (shared with AngleBasedLSCM) - auto parts = detail::lscm::buildSystem(levelMesh, localPins); + auto parts = detail::lscm::BuildSystem(levelMesh, localPins); auto& A = parts.A; auto& b = parts.b; auto& freeIdxTable = parts.freeIdxTable; @@ -844,7 +845,7 @@ auto solveLSCMLevel(const typename HalfEdgeMesh::Pointer& levelMesh, continue; } auto freeIdx = freeIdxTable.at(v->idx); - auto origIdx = level.localToOriginal[v->idx]; + auto origIdx = level.local_to_original[v->idx]; if (const auto& guess = (*initialGuess)[origIdx]) { x0(2 * freeIdx, 0) = (*guess)[0]; x0(2 * freeIdx + 1, 0) = (*guess)[1]; @@ -919,7 +920,7 @@ auto solveLSCMLevel(const typename HalfEdgeMesh::Pointer& levelMesh, continue; } auto freeIdx = 2 * freeIdxTable.at(v->idx); - auto origIdx = level.localToOriginal[v->idx]; + auto origIdx = level.local_to_original[v->idx]; uvs[origIdx] = Vec(x(freeIdx, 0), x(freeIdx + 1, 0)); } return uvs; @@ -982,7 +983,7 @@ class HierarchicalLSCM * @brief Per-pin entry: (mesh vertex index, target UV). * * A PinMap of size N ≥ 2 specifies an explicit LSCM pin set. Each pin - * survives every decimation level — `DecimationMesh::tryCollapse` rejects + * survives every decimation level — `DecimationMesh::try_collapse` rejects * any collapse that would remove a pinned vertex. */ using PinMap = detail::lscm::PinMap; @@ -993,7 +994,7 @@ class HierarchicalLSCM void set_pins(PinMap pins) { pins_ = std::move(pins); - legacyPinIndices_.reset(); + legacy_pin_indices_.reset(); } /** @@ -1006,26 +1007,40 @@ class HierarchicalLSCM [[deprecated("Use set_pins(PinMap); will be removed in 3.0")]] void setPinnedVertices( std::size_t pin0Idx, std::size_t pin1Idx) { - legacyPinIndices_ = {pin0Idx, pin1Idx}; + legacy_pin_indices_ = {pin0Idx, pin1Idx}; pins_.reset(); } /** @brief Set the vertex ratio between consecutive hierarchy levels (default: 10) */ - void setLevelRatio(std::size_t ratio) + void set_level_ratio(std::size_t ratio) { if (ratio < 2) { - throw std::invalid_argument("HierarchicalLSCM: levelRatio must be >= 2"); + throw std::invalid_argument("HierarchicalLSCM: level_ratio must be >= 2"); } - levelRatio_ = ratio; + level_ratio_ = ratio; } /** @brief Set the minimum vertex count at the coarsest level (default: 100) */ - void setMinCoarseVertices(std::size_t count) + void set_min_coarse_vertices(std::size_t count) { if (count < 3) { - throw std::invalid_argument("HierarchicalLSCM: minCoarseVertices must be >= 3"); + throw std::invalid_argument("HierarchicalLSCM: min_coarse_vertices must be >= 3"); } - minCoarseVertices_ = count; + min_coarse_vertices_ = count; + } + + /** @deprecated Use `set_level_ratio`; will be removed in 3.0. */ + [[deprecated("Use set_level_ratio; will be removed in 3.0")]] void setLevelRatio( + std::size_t ratio) + { + set_level_ratio(ratio); + } + + /** @deprecated Use `set_min_coarse_vertices`; will be removed in 3.0. */ + [[deprecated("Use set_min_coarse_vertices; will be removed in 3.0")]] void setMinCoarseVertices( + std::size_t count) + { + set_min_coarse_vertices(count); } /** @@ -1045,13 +1060,13 @@ class HierarchicalLSCM if (pins_) { pins = *pins_; detail::lscm::ValidatePins(mesh, pins); - } else if (legacyPinIndices_) { - pins = detail::lscm::AutoPlacePair(mesh, legacyPinIndices_->first, - legacyPinIndices_->second); + } else if (legacy_pin_indices_) { + pins = detail::lscm::AutoPlacePair(mesh, legacy_pin_indices_->first, + legacy_pin_indices_->second); } else { pins = detail::lscm::AutoSelectPins(mesh); } - ComputeImpl(mesh, pins, levelRatio_, minCoarseVertices_); + ComputeImpl(mesh, pins, level_ratio_, min_coarse_vertices_); } /** @@ -1141,7 +1156,7 @@ class HierarchicalLSCM // Build mesh hierarchy auto [levels, collapsesByLevel] = - detail::hlscm::buildHierarchy(mesh, pinIndices, levelRatio, minCoarseVerts); + detail::hlscm::BuildHierarchy(mesh, pinIndices, levelRatio, minCoarseVerts); if (levels.size() <= 1) { // Mesh too small for hierarchy — single-level LSCM solve. @@ -1156,18 +1171,18 @@ class HierarchicalLSCM // Solve coarsest level (last in the array) auto coarsestIdx = levels.size() - 1; - auto coarseMesh = detail::hlscm::buildLevelMesh(levels[coarsestIdx]); + auto coarseMesh = detail::hlscm::BuildLevelMesh(levels[coarsestIdx]); ComputeMeshAngles(coarseMesh); - auto uvs = detail::hlscm::solveLSCMLevel(coarseMesh, levels[coarsestIdx], pins, + auto uvs = detail::hlscm::SolveLSCMLevel(coarseMesh, levels[coarsestIdx], pins, origVertCount, nullptr); // Prolongate and refine at each finer level for (std::size_t k = coarsestIdx; k-- > 0;) { // Prolongate UVs from level k+1 to level k - uvs = detail::hlscm::prolongateUVs(std::move(uvs), collapsesByLevel[k]); + uvs = detail::hlscm::ProlongateUVs(std::move(uvs), collapsesByLevel[k]); // Build level mesh - auto levelMesh = detail::hlscm::buildLevelMesh(levels[k]); + auto levelMesh = detail::hlscm::BuildLevelMesh(levels[k]); if (k == 0) { // Finest level: use original mesh angles (may be ABF-optimized) @@ -1178,7 +1193,7 @@ class HierarchicalLSCM } // Solve with initial guess - uvs = detail::hlscm::solveLSCMLevel(levelMesh, levels[k], pins, + uvs = detail::hlscm::SolveLSCMLevel(levelMesh, levels[k], pins, origVertCount, &uvs); } @@ -1195,11 +1210,11 @@ class HierarchicalLSCM /** Optional explicit pin set configured via `set_pins()`. */ std::optional pins_; /** Deprecated: legacy two-pin index pair set via `setPinnedVertices`. */ - std::optional> legacyPinIndices_; + std::optional> legacy_pin_indices_; /** Ratio of vertices between consecutive hierarchy levels */ - std::size_t levelRatio_{10}; + std::size_t level_ratio_{10}; /** Minimum vertex count for the coarsest hierarchy level */ - std::size_t minCoarseVertices_{100}; + std::size_t min_coarse_vertices_{100}; }; } // namespace OpenABF diff --git a/include/OpenABF/detail/LSCMSystem.hpp b/include/OpenABF/detail/LSCMSystem.hpp index a71fa38..3d06a86 100644 --- a/include/OpenABF/detail/LSCMSystem.hpp +++ b/include/OpenABF/detail/LSCMSystem.hpp @@ -68,8 +68,8 @@ void ValidatePins(const typename MeshType::Pointer& mesh, const PinMap& pins) * either index is out of range. */ template -auto AutoPlacePair(const typename MeshType::Pointer& mesh, std::size_t p0Idx, - std::size_t p1Idx) -> PinMap +auto AutoPlacePair(const typename MeshType::Pointer& mesh, std::size_t p0Idx, std::size_t p1Idx) + -> PinMap { const auto numVerts = mesh->num_vertices(); if (p0Idx >= numVerts || p1Idx >= numVerts) { @@ -125,7 +125,7 @@ auto AutoSelectPins(const typename MeshType::Pointer& mesh) -> PinMap } /** - * @brief Outputs of `buildSystem`: the LSCM least-squares system for a mesh + * @brief Outputs of `BuildSystem`: the LSCM least-squares system for a mesh * with N pinned vertices (N ≥ 2). * * Layout: `A` is `(2·numFaces) × (2·numFree)`, `b` is `(2·numFaces) × 1`, @@ -161,7 +161,7 @@ struct SystemParts { * already stored on the mesh. */ template -auto buildSystem(const typename MeshType::Pointer& mesh, const PinMap& pins) -> SystemParts +auto BuildSystem(const typename MeshType::Pointer& mesh, const PinMap& pins) -> SystemParts { using Triplet = Eigen::Triplet; using SparseMatrix = Eigen::SparseMatrix; diff --git a/single_include/OpenABF/OpenABF.hpp b/single_include/OpenABF/OpenABF.hpp index 5759a62..122587b 100644 --- a/single_include/OpenABF/OpenABF.hpp +++ b/single_include/OpenABF/OpenABF.hpp @@ -2543,10 +2543,24 @@ class ABF using Mesh = MeshType; /** @brief Set the maximum number of iterations */ - void setMaxIterations(const std::size_t it) { maxIters_ = it; } + void set_max_iterations(const std::size_t it) { max_iters_ = it; } /** @brief Set the gradient convergence threshold */ - void setGradientThreshold(T t) { gradThreshold_ = t; } + void set_gradient_threshold(T t) { grad_threshold_ = t; } + + /** @deprecated Use `set_max_iterations`; will be removed in 3.0. */ + [[deprecated("Use set_max_iterations; will be removed in 3.0")]] void setMaxIterations( + const std::size_t it) + { + set_max_iterations(it); + } + + /** @deprecated Use `set_gradient_threshold`; will be removed in 3.0. */ + [[deprecated("Use set_gradient_threshold; will be removed in 3.0")]] void setGradientThreshold( + T t) + { + set_gradient_threshold(t); + } /** * @brief Get the mesh gradient @@ -2565,7 +2579,7 @@ class ABF /** @copydoc ABF::Compute */ void compute(typename Mesh::Pointer& mesh) { - Compute(mesh, iters_, grad_, maxIters_, gradThreshold_); + Compute(mesh, iters_, grad_, max_iters_, grad_threshold_); } /** @@ -2744,9 +2758,9 @@ class ABF /** Number of executed iterations */ std::size_t iters_{0}; /** Max iterations */ - std::size_t maxIters_{10}; + std::size_t max_iters_{10}; /** Gradient convergence threshold */ - T gradThreshold_{0.001}; + T grad_threshold_{0.001}; }; } // namespace OpenABF @@ -2805,10 +2819,24 @@ class ABFPlusPlus using Mesh = MeshType; /** @brief Set the maximum number of iterations */ - void setMaxIterations(std::size_t it) { maxIters_ = it; } + void set_max_iterations(std::size_t it) { max_iters_ = it; } /** @brief Set the gradient convergence threshold */ - void setGradientThreshold(T t) { gradThreshold_ = t; } + void set_gradient_threshold(T t) { grad_threshold_ = t; } + + /** @deprecated Use `set_max_iterations`; will be removed in 3.0. */ + [[deprecated("Use set_max_iterations; will be removed in 3.0")]] void setMaxIterations( + std::size_t it) + { + set_max_iterations(it); + } + + /** @deprecated Use `set_gradient_threshold`; will be removed in 3.0. */ + [[deprecated("Use set_gradient_threshold; will be removed in 3.0")]] void setGradientThreshold( + T t) + { + set_gradient_threshold(t); + } /** * @brief Get the mesh gradient @@ -2827,7 +2855,7 @@ class ABFPlusPlus /** @copydoc ABFPlusPlus::Compute */ void compute(typename Mesh::Pointer& mesh) { - Compute(mesh, iters_, grad_, maxIters_, gradThreshold_); + Compute(mesh, iters_, grad_, max_iters_, grad_threshold_); } /** @@ -3030,9 +3058,9 @@ class ABFPlusPlus /** Number of executed iterations */ std::size_t iters_{0}; /** Max iterations */ - std::size_t maxIters_{10}; + std::size_t max_iters_{10}; /** Gradient convergence threshold */ - T gradThreshold_{0.001}; + T grad_threshold_{0.001}; }; } // namespace OpenABF @@ -3133,8 +3161,8 @@ void ValidatePins(const typename MeshType::Pointer& mesh, const PinMap& pins) * either index is out of range. */ template -auto AutoPlacePair(const typename MeshType::Pointer& mesh, std::size_t p0Idx, - std::size_t p1Idx) -> PinMap +auto AutoPlacePair(const typename MeshType::Pointer& mesh, std::size_t p0Idx, std::size_t p1Idx) + -> PinMap { const auto numVerts = mesh->num_vertices(); if (p0Idx >= numVerts || p1Idx >= numVerts) { @@ -3190,7 +3218,7 @@ auto AutoSelectPins(const typename MeshType::Pointer& mesh) -> PinMap } /** - * @brief Outputs of `buildSystem`: the LSCM least-squares system for a mesh + * @brief Outputs of `BuildSystem`: the LSCM least-squares system for a mesh * with N pinned vertices (N ≥ 2). * * Layout: `A` is `(2·numFaces) × (2·numFree)`, `b` is `(2·numFaces) × 1`, @@ -3226,7 +3254,7 @@ struct SystemParts { * already stored on the mesh. */ template -auto buildSystem(const typename MeshType::Pointer& mesh, const PinMap& pins) -> SystemParts +auto BuildSystem(const typename MeshType::Pointer& mesh, const PinMap& pins) -> SystemParts { using Triplet = Eigen::Triplet; using SparseMatrix = Eigen::SparseMatrix; @@ -3475,7 +3503,7 @@ class AngleBasedLSCM void set_pins(PinMap pins) { pins_ = std::move(pins); - legacyPinIndices_.reset(); + legacy_pin_indices_.reset(); } /** @@ -3488,7 +3516,7 @@ class AngleBasedLSCM [[deprecated("Use set_pins(PinMap); will be removed in 3.0")]] void setPinnedVertices( std::size_t pin0Idx, std::size_t pin1Idx) { - legacyPinIndices_ = {pin0Idx, pin1Idx}; + legacy_pin_indices_ = {pin0Idx, pin1Idx}; pins_.reset(); } @@ -3497,9 +3525,9 @@ class AngleBasedLSCM { if (pins_) { Compute(mesh, *pins_); - } else if (legacyPinIndices_) { - ComputeImpl(mesh, detail::lscm::AutoPlacePair(mesh, legacyPinIndices_->first, - legacyPinIndices_->second)); + } else if (legacy_pin_indices_) { + ComputeImpl(mesh, detail::lscm::AutoPlacePair(mesh, legacy_pin_indices_->first, + legacy_pin_indices_->second)); } else { Compute(mesh); } @@ -3560,7 +3588,7 @@ class AngleBasedLSCM /** Optional explicit pin set configured via `set_pins()`. */ std::optional pins_; /** Deprecated: legacy two-pin index pair set via `setPinnedVertices`. */ - std::optional> legacyPinIndices_; + std::optional> legacy_pin_indices_; /** * @brief Core solver: build the LSCM system from the PinMap, solve for free @@ -3571,9 +3599,9 @@ class AngleBasedLSCM using SparseMatrix = Eigen::SparseMatrix; using DenseMatrix = Eigen::Matrix; - // LSCM system assembly (shared with HierarchicalLSCM). buildSystem + // LSCM system assembly (shared with HierarchicalLSCM). BuildSystem // does not mutate the mesh; pin UVs are written below. - auto parts = detail::lscm::buildSystem(mesh, pins); + auto parts = detail::lscm::BuildSystem(mesh, pins); // Solve for x auto x = detail::SolveLeastSquares(parts.A, parts.b); @@ -3685,12 +3713,12 @@ struct Quadric { template struct CollapseRecord { /** Index of the removed vertex (in the original/fine mesh) */ - std::size_t vRemoved; + std::size_t v_removed; /** Index of the kept vertex (in the original/fine mesh) */ - std::size_t vKept; - /** Post-collapse triangle containing vRemoved (original vertex indices) */ - std::array containingTri; - /** Barycentric coordinates of vRemoved in containingTri */ + std::size_t v_kept; + /** Post-collapse triangle containing v_removed (original vertex indices) */ + std::array containing_tri; + /** Barycentric coordinates of v_removed in containing_tri */ std::array bary; }; @@ -3704,12 +3732,12 @@ struct HierarchyLevel { /** Face connectivity (each face is 3 level-local indices) */ std::vector> faces; /** Map from level-local vertex index to original (finest) vertex index */ - std::vector localToOriginal; + std::vector local_to_original; /** Map from original vertex index to level-local index. Sized to the * finest-mesh vertex count; vertices not present at this level hold * `std::nullopt`. */ - std::vector> originalToLocal; + std::vector> original_to_local; }; /** @@ -3731,22 +3759,22 @@ class DecimationMesh positions_.resize(nv); alive_.assign(nv, true); - isBoundary_.assign(nv, false); - isPinned_.assign(nv, false); + is_boundary_.assign(nv, false); + is_pinned_.assign(nv, false); quadrics_.resize(nv); for (auto idx : pinIndices) { - isPinned_[idx] = true; + is_pinned_[idx] = true; } for (const auto& v : mesh->vertices()) { positions_[v->idx] = v->pos; - isBoundary_[v->idx] = v->is_boundary(); + is_boundary_[v->idx] = v->is_boundary(); } faces_.reserve(nf); - faceAlive_.reserve(nf); - vertFaces_.resize(nv); + face_alive_.reserve(nf); + vert_faces_.resize(nv); for (const auto& f : mesh->faces()) { auto e0 = f->head; @@ -3755,24 +3783,24 @@ class DecimationMesh std::array tri{e0->vertex->idx, e1->vertex->idx, e2->vertex->idx}; auto fi = faces_.size(); faces_.push_back(tri); - faceAlive_.push_back(true); - vertFaces_[tri[0]].push_back(fi); - vertFaces_[tri[1]].push_back(fi); - vertFaces_[tri[2]].push_back(fi); + face_alive_.push_back(true); + vert_faces_[tri[0]].push_back(fi); + vert_faces_[tri[1]].push_back(fi); + vert_faces_[tri[2]].push_back(fi); } - numAliveVerts_ = nv; - numAliveFaces_ = nf; + num_alive_verts_ = nv; + num_alive_faces_ = nf; computeQuadrics_(); buildEdges_(); } /** Get number of alive vertices */ - [[nodiscard]] auto numAliveVerts() const -> std::size_t { return numAliveVerts_; } + [[nodiscard]] auto num_alive_verts() const -> std::size_t { return num_alive_verts_; } /** Get number of alive faces */ - [[nodiscard]] auto numAliveFaces() const -> std::size_t { return numAliveFaces_; } + [[nodiscard]] auto num_alive_faces() const -> std::size_t { return num_alive_faces_; } /** * @brief Try to collapse edge (vRemove → vKeep), returning a collapse record @@ -3784,29 +3812,29 @@ class DecimationMesh * vertex indices (sorted, unique). Passing a caller- * owned vector here lets the PQ-update path reuse the * allocation across collapses instead of letting - * `vertexNeighbors()` allocate a fresh vector each + * `vertex_neighbors()` allocate a fresh vector each * time. */ - auto tryCollapse(std::size_t vRemove, std::size_t vKeep, - std::vector* outKeepNbrs = nullptr) + auto try_collapse(std::size_t vRemove, std::size_t vKeep, + std::vector* outKeepNbrs = nullptr) -> std::optional> { if (!alive_[vRemove] || !alive_[vKeep]) { return std::nullopt; } - if (isPinned_[vRemove]) { + if (is_pinned_[vRemove]) { return std::nullopt; } // Find shared faces (will be removed) and vRemove-only faces (will be updated) // Reuse scratch storage - auto& sharedFaces = scratchShared_; - auto& removeFaces = scratchRemove_; + auto& sharedFaces = scratch_shared_; + auto& removeFaces = scratch_remove_; sharedFaces.clear(); removeFaces.clear(); - for (auto fi : vertFaces_[vRemove]) { - if (!faceAlive_[fi]) + for (auto fi : vert_faces_[vRemove]) { + if (!face_alive_[fi]) continue; bool hasKeep = false; for (auto vi : faces_[fi]) { @@ -3827,15 +3855,15 @@ class DecimationMesh // Reject collapse of two boundary vertices via an interior edge: // vKeep would inherit two disconnected boundary fans → non-manifold. - if (isBoundary_[vRemove] && isBoundary_[vKeep] && sharedFaces.size() == 2) { + if (is_boundary_[vRemove] && is_boundary_[vKeep] && sharedFaces.size() == 2) { return std::nullopt; } // Link condition: collect sorted unique neighbors of vRemove and vKeep auto fillSortedNeighbors = [&](std::size_t v, std::vector& out) { out.clear(); - for (auto fi : vertFaces_[v]) { - if (!faceAlive_[fi]) + for (auto fi : vert_faces_[v]) { + if (!face_alive_[fi]) continue; for (auto vi : faces_[fi]) { if (vi != v) @@ -3846,17 +3874,18 @@ class DecimationMesh out.erase(std::unique(out.begin(), out.end()), out.end()); }; - fillSortedNeighbors(vRemove, scratchNbrsA_); - fillSortedNeighbors(vKeep, scratchNbrsB_); + fillSortedNeighbors(vRemove, scratch_nbrs_a_); + fillSortedNeighbors(vKeep, scratch_nbrs_b_); // Shared neighbors (excluding vKeep/vRemove from each other's sets) - scratchSharedNbrs_.clear(); - for (auto v : scratchNbrsA_) { - if (v != vKeep && std::binary_search(scratchNbrsB_.begin(), scratchNbrsB_.end(), v)) { - scratchSharedNbrs_.push_back(v); + scratch_shared_nbrs_.clear(); + for (auto v : scratch_nbrs_a_) { + if (v != vKeep && + std::binary_search(scratch_nbrs_b_.begin(), scratch_nbrs_b_.end(), v)) { + scratch_shared_nbrs_.push_back(v); } } - // scratchSharedNbrs_ is already sorted since scratchNbrsA_ is sorted + // scratch_shared_nbrs_ is already sorted since scratch_nbrs_a_ is sorted // Expected shared: opposite vertices of the shared faces (at most 2 entries) std::array expectedShared{}; @@ -3870,10 +3899,10 @@ class DecimationMesh if (numExpected > 1 && expectedShared[0] > expectedShared[1]) std::swap(expectedShared[0], expectedShared[1]); - if (scratchSharedNbrs_.size() != numExpected) + if (scratch_shared_nbrs_.size() != numExpected) return std::nullopt; for (std::size_t i = 0; i < numExpected; ++i) { - if (scratchSharedNbrs_[i] != expectedShared[i]) + if (scratch_shared_nbrs_[i] != expectedShared[i]) return std::nullopt; } @@ -3887,14 +3916,14 @@ class DecimationMesh // maintain a minimum angle above the threshold. // // Collect the full set of post-collapse faces incident to vKeep. - auto& postFaces = scratchPostFaces_; + auto& postFaces = scratch_post_faces_; postFaces.clear(); // Existing vKeep faces (excluding shared faces which will be removed) auto isInShared = [&](std::size_t fi) { return std::find(sharedFaces.begin(), sharedFaces.end(), fi) != sharedFaces.end(); }; - for (auto fi : vertFaces_[vKeep]) { - if (!faceAlive_[fi] || isInShared(fi)) { + for (auto fi : vert_faces_[vKeep]) { + if (!face_alive_[fi] || isInShared(fi)) { continue; } postFaces.push_back(faces_[fi]); @@ -3953,10 +3982,10 @@ class DecimationMesh // Build collapse record with barycentric coordinates // After collapse, face (vRemove, vA, vB) becomes (vKeep, vA, vB). - // Store bary coords of vRemoved's position in the post-collapse triangle. + // Store bary coords of v_removed's position in the post-collapse triangle. CollapseRecord record; - record.vRemoved = vRemove; - record.vKept = vKeep; + record.v_removed = vRemove; + record.v_kept = vKeep; if (!removeFaces.empty()) { // Use the first surviving face; its post-collapse vertices are @@ -3970,20 +3999,20 @@ class DecimationMesh postTri[slot++] = vi; } } - record.containingTri = postTri; + record.containing_tri = postTri; record.bary = computeBarycentric_(positions_[postTri[0]], positions_[postTri[1]], positions_[postTri[2]], positions_[vRemove]); } else { // Edge case: all faces are shared — vertex collapses directly onto vKeep - record.containingTri = {vKeep, vKeep, vKeep}; + record.containing_tri = {vKeep, vKeep, vKeep}; record.bary = {T(1), T(0), T(0)}; } // Execute collapse // Kill shared faces for (auto fi : sharedFaces) { - faceAlive_[fi] = false; - numAliveFaces_--; + face_alive_[fi] = false; + num_alive_faces_--; } // Update removeFaces: replace vRemove with vKeep @@ -3993,30 +4022,30 @@ class DecimationMesh vi = vKeep; } } - vertFaces_[vKeep].push_back(fi); + vert_faces_[vKeep].push_back(fi); } // Mark vRemove as dead alive_[vRemove] = false; - numAliveVerts_--; + num_alive_verts_--; // Propagate boundary status: if vRemove was on the boundary, // vKeep inherits it (it now sits on the mesh boundary). - if (isBoundary_[vRemove]) { - isBoundary_[vKeep] = true; + if (is_boundary_[vRemove]) { + is_boundary_[vKeep] = true; } // Merge quadrics quadrics_[vKeep] += quadrics_[vRemove]; // Compact dead face indices from vKeep's adjacency list - auto& vkFaces = vertFaces_[vKeep]; + auto& vkFaces = vert_faces_[vKeep]; vkFaces.erase(std::remove_if(vkFaces.begin(), vkFaces.end(), - [this](std::size_t fi) { return !faceAlive_[fi]; }), + [this](std::size_t fi) { return !face_alive_[fi]; }), vkFaces.end()); // Fill caller-owned post-collapse neighbor vector (reusing its - // allocation across calls). Equivalent to vertexNeighbors(vKeep) but + // allocation across calls). Equivalent to vertex_neighbors(vKeep) but // without an additional heap allocation. if (outKeepNbrs != nullptr) { outKeepNbrs->clear(); @@ -4036,7 +4065,7 @@ class DecimationMesh } /** Compute collapse cost for edge (v0 → v1): Q_merged evaluated at v1 */ - [[nodiscard]] auto collapseCost(std::size_t v0, std::size_t v1) const -> T + [[nodiscard]] auto collapse_cost(std::size_t v0, std::size_t v1) const -> T { auto Q = quadrics_[v0] + quadrics_[v1]; auto& p = positions_[v1]; @@ -4044,25 +4073,25 @@ class DecimationMesh } /** Check if a vertex is alive */ - [[nodiscard]] auto isAlive(std::size_t v) const -> bool { return alive_[v]; } + [[nodiscard]] auto is_alive(std::size_t v) const -> bool { return alive_[v]; } /** Check if a vertex is collapsible (not pinned, alive) */ - [[nodiscard]] auto isCollapsible(std::size_t v) const -> bool + [[nodiscard]] auto is_collapsible(std::size_t v) const -> bool { - return alive_[v] && !isPinned_[v]; + return alive_[v] && !is_pinned_[v]; } /** Get alive neighbour vertices of `v` (sorted, deduped). * - * Production code path uses `tryCollapse`'s `outKeepNbrs` out-param to + * Production code path uses `try_collapse`'s `outKeepNbrs` out-param to * avoid this method's per-call allocation; this overload is retained * only as the oracle for the `HLSCMInternal.TryCollapse_*` tests. */ - [[nodiscard]] auto vertexNeighbors(std::size_t v) const -> std::vector + [[nodiscard]] auto vertex_neighbors(std::size_t v) const -> std::vector { std::vector nbrs; - for (auto fi : vertFaces_[v]) { - if (!faceAlive_[fi]) + for (auto fi : vert_faces_[v]) { + if (!face_alive_[fi]) continue; for (auto vi : faces_[fi]) { if (vi != v && alive_[vi]) @@ -4078,14 +4107,14 @@ class DecimationMesh [[nodiscard]] auto snapshot() const -> HierarchyLevel { HierarchyLevel level; - level.originalToLocal.assign(alive_.size(), std::nullopt); + level.original_to_local.assign(alive_.size(), std::nullopt); // Build mapping from original indices to level-local indices std::size_t localIdx = 0; for (std::size_t i = 0; i < alive_.size(); ++i) { if (alive_[i]) { - level.originalToLocal[i] = localIdx; - level.localToOriginal.push_back(i); + level.original_to_local[i] = localIdx; + level.local_to_original.push_back(i); level.positions.push_back(positions_[i]); localIdx++; } @@ -4094,12 +4123,12 @@ class DecimationMesh // Remap faces. Every alive face references only alive vertices, so // the unwrap is always safe here. for (std::size_t fi = 0; fi < faces_.size(); ++fi) { - if (!faceAlive_[fi]) { + if (!face_alive_[fi]) { continue; } std::array localTri; for (int j = 0; j < 3; ++j) { - localTri[j] = *level.originalToLocal[faces_[fi][j]]; + localTri[j] = *level.original_to_local[faces_[fi][j]]; } level.faces.push_back(localTri); } @@ -4108,7 +4137,7 @@ class DecimationMesh } /** Rebuild the edge list from alive faces and return it */ - auto rebuildAndGetEdges() -> const std::vector>& + auto rebuild_and_get_edges() -> const std::vector>& { buildEdges_(); return edges_; @@ -4123,7 +4152,7 @@ class DecimationMesh } for (std::size_t fi = 0; fi < faces_.size(); ++fi) { - if (!faceAlive_[fi]) { + if (!face_alive_[fi]) { continue; } auto& tri = faces_[fi]; @@ -4163,7 +4192,7 @@ class DecimationMesh }; for (std::size_t fi = 0; fi < faces_.size(); ++fi) { - if (!faceAlive_[fi]) { + if (!face_alive_[fi]) { continue; } auto& tri = faces_[fi]; @@ -4213,36 +4242,36 @@ class DecimationMesh /** Per-vertex alive flag; collapses set entries to false. */ std::vector alive_; /** Per-vertex boundary flag, copied from the source mesh. */ - std::vector isBoundary_; + std::vector is_boundary_; /** Per-vertex pinned flag; pinned vertices cannot be removed by collapse. */ - std::vector isPinned_; + std::vector is_pinned_; /** Per-vertex QEM quadrics accumulated from incident face planes. */ std::vector> quadrics_; /** Face connectivity: each face is three vertex indices into `positions_`. */ std::vector> faces_; /** Per-face alive flag; collapses retire faces by setting entries to false. */ - std::vector faceAlive_; + std::vector face_alive_; /** Per-vertex incident face list. */ - std::vector> vertFaces_; + std::vector> vert_faces_; /** Count of alive vertices (kept current across collapses). */ - std::size_t numAliveVerts_{0}; + std::size_t num_alive_verts_{0}; /** Count of alive faces (kept current across collapses). */ - std::size_t numAliveFaces_{0}; + std::size_t num_alive_faces_{0}; /** Unique-edge list, rebuilt on demand by `buildEdges_()`. */ std::vector> edges_; /** Scratch buffer: neighbor indices shared between two endpoints. */ - std::vector scratchShared_; + std::vector scratch_shared_; /** Scratch buffer: faces to retire on a collapse. */ - std::vector scratchRemove_; + std::vector scratch_remove_; /** Scratch buffer: post-collapse face rewrites. */ - std::vector> scratchPostFaces_; + std::vector> scratch_post_faces_; /** Scratch buffer: neighbor list of one endpoint. */ - std::vector scratchNbrsA_; + std::vector scratch_nbrs_a_; /** Scratch buffer: neighbor list of the other endpoint. */ - std::vector scratchNbrsB_; - /** Scratch buffer: deduplicated union of `scratchNbrsA_` and `scratchNbrsB_`. */ - std::vector scratchSharedNbrs_; + std::vector scratch_nbrs_b_; + /** Scratch buffer: deduplicated union of `scratch_nbrs_a_` and `scratch_nbrs_b_`. */ + std::vector scratch_shared_nbrs_; }; /** @@ -4252,7 +4281,7 @@ class DecimationMesh * the collapse records needed for prolongation (ordered from finest to coarsest). */ template -auto buildHierarchy(const MeshPtr& mesh, const std::vector& pinIndices, +auto BuildHierarchy(const MeshPtr& mesh, const std::vector& pinIndices, std::size_t levelRatio, std::size_t minCoarseVerts) -> std::pair>, std::vector>>> { @@ -4265,14 +4294,14 @@ auto buildHierarchy(const MeshPtr& mesh, const std::vector& pinIndi std::vector>> collapsesByLevel; - auto targetVerts = dmesh.numAliveVerts(); + auto targetVerts = dmesh.num_alive_verts(); if (targetVerts <= minCoarseVerts) { // Mesh is already small enough — single level return {levels, collapsesByLevel}; } // Reused across collapses to avoid per-collapse heap allocation; populated - // by tryCollapse with vKeep's post-collapse neighbors. + // by try_collapse with vKeep's post-collapse neighbors. std::vector nbrs; while (targetVerts > minCoarseVerts) { @@ -4283,27 +4312,27 @@ auto buildHierarchy(const MeshPtr& mesh, const std::vector& pinIndi using CostEdge = std::pair>; std::priority_queue, std::greater> pq; - const auto& edges = dmesh.rebuildAndGetEdges(); + const auto& edges = dmesh.rebuild_and_get_edges(); for (auto& [a, b] : edges) { // Try collapsing the collapsible vertex towards the other - if (dmesh.isCollapsible(a)) { - pq.push({dmesh.collapseCost(a, b), {a, b}}); + if (dmesh.is_collapsible(a)) { + pq.push({dmesh.collapse_cost(a, b), {a, b}}); } - if (dmesh.isCollapsible(b)) { - pq.push({dmesh.collapseCost(b, a), {b, a}}); + if (dmesh.is_collapsible(b)) { + pq.push({dmesh.collapse_cost(b, a), {b, a}}); } } - while (dmesh.numAliveVerts() > nextTarget && !pq.empty()) { + while (dmesh.num_alive_verts() > nextTarget && !pq.empty()) { auto [cost, edge] = pq.top(); pq.pop(); auto [vRemove, vKeep] = edge; // Skip stale entries whose endpoints have already been collapsed - if (!dmesh.isAlive(vRemove) || !dmesh.isAlive(vKeep)) { + if (!dmesh.is_alive(vRemove) || !dmesh.is_alive(vKeep)) { continue; } - auto record = dmesh.tryCollapse(vRemove, vKeep, &nbrs); + auto record = dmesh.try_collapse(vRemove, vKeep, &nbrs); if (!record) { continue; } @@ -4311,13 +4340,13 @@ auto buildHierarchy(const MeshPtr& mesh, const std::vector& pinIndi levelCollapses.push_back(*record); // Add new edges involving vKeep to the priority queue using the - // post-collapse neighbor list populated by tryCollapse. + // post-collapse neighbor list populated by try_collapse. for (auto nb : nbrs) { - if (dmesh.isCollapsible(nb)) { - pq.push({dmesh.collapseCost(nb, vKeep), {nb, vKeep}}); + if (dmesh.is_collapsible(nb)) { + pq.push({dmesh.collapse_cost(nb, vKeep), {nb, vKeep}}); } - if (dmesh.isCollapsible(vKeep)) { - pq.push({dmesh.collapseCost(vKeep, nb), {vKeep, nb}}); + if (dmesh.is_collapsible(vKeep)) { + pq.push({dmesh.collapse_cost(vKeep, nb), {vKeep, nb}}); } } } @@ -4328,7 +4357,7 @@ auto buildHierarchy(const MeshPtr& mesh, const std::vector& pinIndi collapsesByLevel.push_back(std::move(levelCollapses)); levels.push_back(dmesh.snapshot()); - targetVerts = dmesh.numAliveVerts(); + targetVerts = dmesh.num_alive_verts(); } return {levels, collapsesByLevel}; @@ -4338,7 +4367,7 @@ auto buildHierarchy(const MeshPtr& mesh, const std::vector& pinIndi * @brief Build a HalfEdgeMesh from a hierarchy level */ template -auto buildLevelMesh(const HierarchyLevel& level) -> typename HalfEdgeMesh::Pointer +auto BuildLevelMesh(const HierarchyLevel& level) -> typename HalfEdgeMesh::Pointer { auto mesh = HalfEdgeMesh::New(); @@ -4373,7 +4402,7 @@ using UVVector = std::vector>>; * @param collapses Collapse records for this level transition. */ template -auto prolongateUVs(UVVector uvs, const std::vector>& collapses) -> UVVector +auto ProlongateUVs(UVVector uvs, const std::vector>& collapses) -> UVVector { // Undo collapses in reverse order — the last collapse applied is the first // we need to undo to recover the next-finer level's UVs. All three @@ -4381,9 +4410,9 @@ auto prolongateUVs(UVVector uvs, const std::vector>& collap // dereference them: they survived the collapse we're undoing. for (auto it = collapses.rbegin(); it != collapses.rend(); ++it) { auto& rec = *it; - auto& tri = rec.containingTri; + auto& tri = rec.containing_tri; - uvs[rec.vRemoved] = + uvs[rec.v_removed] = *uvs[tri[0]] * rec.bary[0] + *uvs[tri[1]] * rec.bary[1] + *uvs[tri[2]] * rec.bary[2]; } @@ -4402,7 +4431,7 @@ auto prolongateUVs(UVVector uvs, const std::vector>& collap * not present at this level remain `std::nullopt`. */ template -auto solveLSCMLevel(const typename HalfEdgeMesh::Pointer& levelMesh, +auto SolveLSCMLevel(const typename HalfEdgeMesh::Pointer& levelMesh, const detail::hlscm::HierarchyLevel& level, const detail::lscm::PinMap& origPins, std::size_t origVertCount, const UVVector* initialGuess) -> UVVector @@ -4416,7 +4445,7 @@ auto solveLSCMLevel(const typename HalfEdgeMesh::Pointer& levelMesh, auto numFree = numVerts - numFixed; // Map each original pin idx to its level-local idx. Pins are guaranteed - // to survive every decimation level (DecimationMesh::tryCollapse rejects + // to survive every decimation level (DecimationMesh::try_collapse rejects // collapses of pinned vertices), so this unwrap is expected to succeed. // Throw a meaningful exception rather than std::bad_optional_access if a // future refactor ever breaks the invariant. @@ -4425,7 +4454,7 @@ auto solveLSCMLevel(const typename HalfEdgeMesh::Pointer& levelMesh, std::unordered_set localPinIdx; localPinIdx.reserve(numFixed); for (const auto& [origIdx, uv] : origPins) { - const auto& localOpt = level.originalToLocal[origIdx]; + const auto& localOpt = level.original_to_local[origIdx]; if (!localOpt.has_value()) { throw SolverException( "HLSCM: pinned vertex was removed during decimation (invariant violated)"); @@ -4436,7 +4465,7 @@ auto solveLSCMLevel(const typename HalfEdgeMesh::Pointer& levelMesh, } // Pin placement + LSCM system assembly (shared with AngleBasedLSCM) - auto parts = detail::lscm::buildSystem(levelMesh, localPins); + auto parts = detail::lscm::BuildSystem(levelMesh, localPins); auto& A = parts.A; auto& b = parts.b; auto& freeIdxTable = parts.freeIdxTable; @@ -4452,7 +4481,7 @@ auto solveLSCMLevel(const typename HalfEdgeMesh::Pointer& levelMesh, continue; } auto freeIdx = freeIdxTable.at(v->idx); - auto origIdx = level.localToOriginal[v->idx]; + auto origIdx = level.local_to_original[v->idx]; if (const auto& guess = (*initialGuess)[origIdx]) { x0(2 * freeIdx, 0) = (*guess)[0]; x0(2 * freeIdx + 1, 0) = (*guess)[1]; @@ -4527,7 +4556,7 @@ auto solveLSCMLevel(const typename HalfEdgeMesh::Pointer& levelMesh, continue; } auto freeIdx = 2 * freeIdxTable.at(v->idx); - auto origIdx = level.localToOriginal[v->idx]; + auto origIdx = level.local_to_original[v->idx]; uvs[origIdx] = Vec(x(freeIdx, 0), x(freeIdx + 1, 0)); } return uvs; @@ -4590,7 +4619,7 @@ class HierarchicalLSCM * @brief Per-pin entry: (mesh vertex index, target UV). * * A PinMap of size N ≥ 2 specifies an explicit LSCM pin set. Each pin - * survives every decimation level — `DecimationMesh::tryCollapse` rejects + * survives every decimation level — `DecimationMesh::try_collapse` rejects * any collapse that would remove a pinned vertex. */ using PinMap = detail::lscm::PinMap; @@ -4601,7 +4630,7 @@ class HierarchicalLSCM void set_pins(PinMap pins) { pins_ = std::move(pins); - legacyPinIndices_.reset(); + legacy_pin_indices_.reset(); } /** @@ -4614,26 +4643,40 @@ class HierarchicalLSCM [[deprecated("Use set_pins(PinMap); will be removed in 3.0")]] void setPinnedVertices( std::size_t pin0Idx, std::size_t pin1Idx) { - legacyPinIndices_ = {pin0Idx, pin1Idx}; + legacy_pin_indices_ = {pin0Idx, pin1Idx}; pins_.reset(); } /** @brief Set the vertex ratio between consecutive hierarchy levels (default: 10) */ - void setLevelRatio(std::size_t ratio) + void set_level_ratio(std::size_t ratio) { if (ratio < 2) { - throw std::invalid_argument("HierarchicalLSCM: levelRatio must be >= 2"); + throw std::invalid_argument("HierarchicalLSCM: level_ratio must be >= 2"); } - levelRatio_ = ratio; + level_ratio_ = ratio; } /** @brief Set the minimum vertex count at the coarsest level (default: 100) */ - void setMinCoarseVertices(std::size_t count) + void set_min_coarse_vertices(std::size_t count) { if (count < 3) { - throw std::invalid_argument("HierarchicalLSCM: minCoarseVertices must be >= 3"); + throw std::invalid_argument("HierarchicalLSCM: min_coarse_vertices must be >= 3"); } - minCoarseVertices_ = count; + min_coarse_vertices_ = count; + } + + /** @deprecated Use `set_level_ratio`; will be removed in 3.0. */ + [[deprecated("Use set_level_ratio; will be removed in 3.0")]] void setLevelRatio( + std::size_t ratio) + { + set_level_ratio(ratio); + } + + /** @deprecated Use `set_min_coarse_vertices`; will be removed in 3.0. */ + [[deprecated("Use set_min_coarse_vertices; will be removed in 3.0")]] void setMinCoarseVertices( + std::size_t count) + { + set_min_coarse_vertices(count); } /** @@ -4653,13 +4696,13 @@ class HierarchicalLSCM if (pins_) { pins = *pins_; detail::lscm::ValidatePins(mesh, pins); - } else if (legacyPinIndices_) { - pins = detail::lscm::AutoPlacePair(mesh, legacyPinIndices_->first, - legacyPinIndices_->second); + } else if (legacy_pin_indices_) { + pins = detail::lscm::AutoPlacePair(mesh, legacy_pin_indices_->first, + legacy_pin_indices_->second); } else { pins = detail::lscm::AutoSelectPins(mesh); } - ComputeImpl(mesh, pins, levelRatio_, minCoarseVertices_); + ComputeImpl(mesh, pins, level_ratio_, min_coarse_vertices_); } /** @@ -4749,7 +4792,7 @@ class HierarchicalLSCM // Build mesh hierarchy auto [levels, collapsesByLevel] = - detail::hlscm::buildHierarchy(mesh, pinIndices, levelRatio, minCoarseVerts); + detail::hlscm::BuildHierarchy(mesh, pinIndices, levelRatio, minCoarseVerts); if (levels.size() <= 1) { // Mesh too small for hierarchy — single-level LSCM solve. @@ -4764,18 +4807,18 @@ class HierarchicalLSCM // Solve coarsest level (last in the array) auto coarsestIdx = levels.size() - 1; - auto coarseMesh = detail::hlscm::buildLevelMesh(levels[coarsestIdx]); + auto coarseMesh = detail::hlscm::BuildLevelMesh(levels[coarsestIdx]); ComputeMeshAngles(coarseMesh); - auto uvs = detail::hlscm::solveLSCMLevel(coarseMesh, levels[coarsestIdx], pins, + auto uvs = detail::hlscm::SolveLSCMLevel(coarseMesh, levels[coarsestIdx], pins, origVertCount, nullptr); // Prolongate and refine at each finer level for (std::size_t k = coarsestIdx; k-- > 0;) { // Prolongate UVs from level k+1 to level k - uvs = detail::hlscm::prolongateUVs(std::move(uvs), collapsesByLevel[k]); + uvs = detail::hlscm::ProlongateUVs(std::move(uvs), collapsesByLevel[k]); // Build level mesh - auto levelMesh = detail::hlscm::buildLevelMesh(levels[k]); + auto levelMesh = detail::hlscm::BuildLevelMesh(levels[k]); if (k == 0) { // Finest level: use original mesh angles (may be ABF-optimized) @@ -4786,7 +4829,7 @@ class HierarchicalLSCM } // Solve with initial guess - uvs = detail::hlscm::solveLSCMLevel(levelMesh, levels[k], pins, + uvs = detail::hlscm::SolveLSCMLevel(levelMesh, levels[k], pins, origVertCount, &uvs); } @@ -4803,11 +4846,11 @@ class HierarchicalLSCM /** Optional explicit pin set configured via `set_pins()`. */ std::optional pins_; /** Deprecated: legacy two-pin index pair set via `setPinnedVertices`. */ - std::optional> legacyPinIndices_; + std::optional> legacy_pin_indices_; /** Ratio of vertices between consecutive hierarchy levels */ - std::size_t levelRatio_{10}; + std::size_t level_ratio_{10}; /** Minimum vertex count for the coarsest hierarchy level */ - std::size_t minCoarseVertices_{100}; + std::size_t min_coarse_vertices_{100}; }; } // namespace OpenABF diff --git a/tests/src/TestParameterization.cpp b/tests/src/TestParameterization.cpp index 506a6f9..326dec1 100644 --- a/tests/src/TestParameterization.cpp +++ b/tests/src/TestParameterization.cpp @@ -196,7 +196,7 @@ TEST(Parameterizations, ABF_MaxIters) auto mesh = ConstructPyramid(); ABFType abf; - abf.setMaxIterations(1); + abf.set_max_iterations(1); abf.compute(mesh); EXPECT_EQ(abf.iterations(), 1u); @@ -213,8 +213,8 @@ TEST(Parameterizations, ABFPlusPlus_TightThreshold) auto mesh_tight = ConstructPyramid(); ABFType abf_tight; - abf_tight.setGradientThreshold(1e-6f); - abf_tight.setMaxIterations(100); + abf_tight.set_gradient_threshold(1e-6f); + abf_tight.set_max_iterations(100); abf_tight.compute(mesh_tight); EXPECT_LE(abf_tight.gradient(), abf_default.gradient()); @@ -227,7 +227,7 @@ TEST(Parameterizations, ABFPlusPlus_LooseThreshold) auto mesh = ConstructPyramid(); ABFType abf; - abf.setGradientThreshold(1e6f); + abf.set_gradient_threshold(1e6f); abf.compute(mesh); EXPECT_EQ(abf.iterations(), 0u); @@ -487,17 +487,17 @@ TEST(HLSCM, WavySurface) TEST(HLSCM, InstanceAPILevelRatio) { - // Verify that setLevelRatio/setMinCoarseVertices produce valid results AND + // Verify that set_level_ratio/set_min_coarse_vertices produce valid results AND // that the levelRatio parameter actually affects hierarchy construction. - // A no-op setLevelRatio (e.g., parameter ignored) would cause the + // A no-op set_level_ratio (e.g., parameter ignored) would cause the // hierarchy-depth assertion below to fail because both ratios would // produce identical hierarchies. using HLSCM = HierarchicalLSCM; auto mesh = ConstructWavySurface(20, 20); HLSCM hlscm; - hlscm.setLevelRatio(4); - hlscm.setMinCoarseVertices(50); + hlscm.set_level_ratio(4); + hlscm.set_min_coarse_vertices(50); hlscm.compute(mesh); for (std::size_t v = 0; v < mesh->num_vertices(); ++v) { @@ -507,7 +507,7 @@ TEST(HLSCM, InstanceAPILevelRatio) EXPECT_FLOAT_EQ(pos[2], 0.f); } - // Directly invoke buildHierarchy with two clearly different levelRatio + // Directly invoke BuildHierarchy with two clearly different levelRatio // values on the same input. A larger ratio decimates more aggressively // per level and should produce a hierarchy with fewer levels (each level // also having a smaller alive-vertex count) than a small ratio. @@ -519,9 +519,9 @@ TEST(HLSCM, InstanceAPILevelRatio) const std::vector pinIndices{pin0, pin1}; auto [levelsSmall, _csmall] = - buildHierarchy(mesh2, pinIndices, /*levelRatio=*/2, minCoarseVerts); + BuildHierarchy(mesh2, pinIndices, /*levelRatio=*/2, minCoarseVerts); auto [levelsLarge, _clarge] = - buildHierarchy(mesh2, pinIndices, /*levelRatio=*/8, minCoarseVerts); + BuildHierarchy(mesh2, pinIndices, /*levelRatio=*/8, minCoarseVerts); // Sanity: both hierarchies have at least the finest level ASSERT_GE(levelsSmall.size(), 1u); @@ -540,20 +540,20 @@ TEST(HLSCM, InstanceAPILevelRatio) TEST(HLSCM, MultiLevelHierarchy) { // Verify a multi-level hierarchy is actually built by calling - // detail::hlscm::buildHierarchy directly and asserting on levels.size(). + // detail::hlscm::BuildHierarchy directly and asserting on levels.size(). using HLSCM = OpenABF::HierarchicalLSCM; auto mesh = ConstructWavySurface(20, 20); // Match HLSCM defaults except force a small minCoarseVerts so we get >1 level - auto [levels, collapsesByLevel] = OpenABF::detail::hlscm::buildHierarchy( + auto [levels, collapsesByLevel] = OpenABF::detail::hlscm::BuildHierarchy( mesh, std::vector{0, 1}, /*levelRatio=*/10, /*minCoarseVerts=*/10); EXPECT_GE(levels.size(), std::size_t(2)) - << "buildHierarchy produced only " << levels.size() << " level(s)"; + << "BuildHierarchy produced only " << levels.size() << " level(s)"; // Sanity-check that the full pipeline still runs end-to-end on this mesh HLSCM hlscm; - hlscm.setMinCoarseVertices(10); + hlscm.set_min_coarse_vertices(10); ASSERT_NO_THROW(hlscm.compute(mesh)); // All UVs should be finite and z=0 @@ -845,19 +845,19 @@ TEST(HLSCM, FlatGridNoDistortion) TEST(HLSCM, LevelRatioBoundaryValues) { - // setLevelRatio(0) and setLevelRatio(1) must throw std::invalid_argument + // set_level_ratio(0) and set_level_ratio(1) must throw std::invalid_argument // because they would cause division-by-zero or degenerate hierarchies. using HLSCM = HierarchicalLSCM; HLSCM hlscm; - EXPECT_THROW(hlscm.setLevelRatio(0), std::invalid_argument); - EXPECT_THROW(hlscm.setLevelRatio(1), std::invalid_argument); - EXPECT_NO_THROW(hlscm.setLevelRatio(2)); - - // setMinCoarseVertices(0), (1), (2) must throw; 3 is the minimum valid value - EXPECT_THROW(hlscm.setMinCoarseVertices(0), std::invalid_argument); - EXPECT_THROW(hlscm.setMinCoarseVertices(1), std::invalid_argument); - EXPECT_THROW(hlscm.setMinCoarseVertices(2), std::invalid_argument); - EXPECT_NO_THROW(hlscm.setMinCoarseVertices(3)); + EXPECT_THROW(hlscm.set_level_ratio(0), std::invalid_argument); + EXPECT_THROW(hlscm.set_level_ratio(1), std::invalid_argument); + EXPECT_NO_THROW(hlscm.set_level_ratio(2)); + + // set_min_coarse_vertices(0), (1), (2) must throw; 3 is the minimum valid value + EXPECT_THROW(hlscm.set_min_coarse_vertices(0), std::invalid_argument); + EXPECT_THROW(hlscm.set_min_coarse_vertices(1), std::invalid_argument); + EXPECT_THROW(hlscm.set_min_coarse_vertices(2), std::invalid_argument); + EXPECT_NO_THROW(hlscm.set_min_coarse_vertices(3)); } TEST(HLSCM, DoubleOnHemisphere) @@ -878,7 +878,7 @@ TEST(HLSCM, DoubleOnHemisphere) TEST(HLSCMInternal, DecimationMesh_RejectsPinnedVertex) { - // Directly unit-test detail::hlscm::DecimationMesh: tryCollapse must + // Directly unit-test detail::hlscm::DecimationMesh: try_collapse must // reject the collapse when the vertex-to-remove is pinned, even if the // edge is geometrically valid. using namespace OpenABF::detail::hlscm; @@ -900,11 +900,12 @@ TEST(HLSCMInternal, DecimationMesh_RejectsPinnedVertex) dm.build(mesh, std::vector{pin0, pin1}); // Attempting to remove a pinned vertex (pin0=0 → vertex 1) must return nullopt - auto result = dm.tryCollapse(pin0, 1); - EXPECT_FALSE(result.has_value()) << "tryCollapse should reject collapse when vRemove is pinned"; + auto result = dm.try_collapse(pin0, 1); + EXPECT_FALSE(result.has_value()) + << "try_collapse should reject collapse when vRemove is pinned"; // Attempting to remove a non-pinned vertex should succeed (may return a valid record) - auto result2 = dm.tryCollapse(3, 0); + auto result2 = dm.try_collapse(3, 0); // vertex 3 is the apex (non-boundary, non-pinned) — collapse may succeed or be // rejected on geometric grounds, but must never crash (void)result2; @@ -912,8 +913,8 @@ TEST(HLSCMInternal, DecimationMesh_RejectsPinnedVertex) TEST(HLSCMInternal, TryCollapse_OutKeepNbrsMatchesVertexNeighbors) { - // tryCollapse's outKeepNbrs out-param is the production hot-path replacement - // for calling vertexNeighbors(vKeep) after every collapse. They must agree + // try_collapse's outKeepNbrs out-param is the production hot-path replacement + // for calling vertex_neighbors(vKeep) after every collapse. They must agree // for every successful collapse — that's the invariant the perf optimization // rests on. using namespace OpenABF::detail::hlscm; @@ -929,24 +930,24 @@ TEST(HLSCMInternal, TryCollapse_OutKeepNbrsMatchesVertexNeighbors) std::vector outNbrs; std::size_t successful = 0; for (std::size_t v = 1; v < mesh->num_vertices() && successful < 16; ++v) { - if (!dm.isCollapsible(v)) { + if (!dm.is_collapsible(v)) { continue; } // Try every available neighbour as the collapse target until one succeeds. - auto candidates = dm.vertexNeighbors(v); + auto candidates = dm.vertex_neighbors(v); for (auto target : candidates) { - if (!dm.isAlive(target)) { + if (!dm.is_alive(target)) { continue; } - auto rec = dm.tryCollapse(v, target, &outNbrs); + auto rec = dm.try_collapse(v, target, &outNbrs); if (!rec) { continue; } - // The post-collapse neighbour set computed inside tryCollapse must + // The post-collapse neighbour set computed inside try_collapse must // equal what the standalone oracle would return on the same state. - auto oracle = dm.vertexNeighbors(target); + auto oracle = dm.vertex_neighbors(target); EXPECT_EQ(outNbrs, oracle) << "After collapsing " << v << " into " << target - << ": outKeepNbrs disagrees with vertexNeighbors(vKeep)"; + << ": outKeepNbrs disagrees with vertex_neighbors(vKeep)"; ++successful; break; } @@ -966,7 +967,7 @@ TEST(HLSCMInternal, ProlongateUVs_MultiLevelCoverage) constexpr std::size_t pin0 = 0; constexpr std::size_t pin1 = 11; - auto [levels, collapsesByLevel] = OpenABF::detail::hlscm::buildHierarchy( + auto [levels, collapsesByLevel] = OpenABF::detail::hlscm::BuildHierarchy( mesh, std::vector{pin0, pin1}, /*levelRatio=*/3, /*minCoarseVerts=*/5); ASSERT_GE(levels.size(), std::size_t(3)) << "Expected at least 3 hierarchy levels"; @@ -974,15 +975,15 @@ TEST(HLSCMInternal, ProlongateUVs_MultiLevelCoverage) const auto origVertCount = mesh->num_vertices(); OpenABF::detail::hlscm::UVVector uvs(origVertCount); const auto& coarsest = levels.back(); - for (std::size_t li = 0; li < coarsest.localToOriginal.size(); ++li) { - auto origIdx = coarsest.localToOriginal[li]; + for (std::size_t li = 0; li < coarsest.local_to_original.size(); ++li) { + auto origIdx = coarsest.local_to_original[li]; uvs[origIdx] = OpenABF::Vec(static_cast(origIdx), -static_cast(origIdx)); } // Prolongate through every level transition (coarsest → finest). for (std::size_t k = levels.size() - 1; k-- > 0;) { - uvs = OpenABF::detail::hlscm::prolongateUVs(std::move(uvs), collapsesByLevel[k]); + uvs = OpenABF::detail::hlscm::ProlongateUVs(std::move(uvs), collapsesByLevel[k]); } // Every finest-level vertex must now have a UV — the post-prolongation @@ -995,11 +996,11 @@ TEST(HLSCMInternal, ProlongateUVs_MultiLevelCoverage) TEST(HLSCMInternal, BuildHierarchy_LevelCount) { - // Directly invoke detail::hlscm::buildHierarchy on a 20×20 wavy surface + // Directly invoke detail::hlscm::BuildHierarchy on a 20×20 wavy surface // and assert that the returned hierarchy has the expected structure: // - At least 3 levels at minCoarseVerts=10 // - Consecutive-level vertex-count ratio approximately matches levelRatio - // - localToOriginal and originalToLocal are consistent inverses + // - local_to_original and original_to_local are consistent inverses // - Pin vertices survive at every level using HLSCM = OpenABF::HierarchicalLSCM; auto mesh = ConstructWavySurface(20, 20); @@ -1009,7 +1010,7 @@ TEST(HLSCMInternal, BuildHierarchy_LevelCount) constexpr std::size_t levelRatio = 4; constexpr std::size_t minCoarseVerts = 10; - auto [levels, collapsesByLevel] = OpenABF::detail::hlscm::buildHierarchy( + auto [levels, collapsesByLevel] = OpenABF::detail::hlscm::BuildHierarchy( mesh, std::vector{pin0, pin1}, levelRatio, minCoarseVerts); // 20x20 = 400 verts, ratio 4, min 10 → 400, 100, 25, 10 → 4 levels (≥3) @@ -1024,8 +1025,8 @@ TEST(HLSCMInternal, BuildHierarchy_LevelCount) // (within a generous tolerance because decimation may stop early for // geometric reasons and the last step clamps to minCoarseVerts) for (std::size_t k = 0; k + 1 < levels.size(); ++k) { - auto prev = levels[k].localToOriginal.size(); - auto next = levels[k + 1].localToOriginal.size(); + auto prev = levels[k].local_to_original.size(); + auto next = levels[k + 1].local_to_original.size(); ASSERT_GT(prev, next) << "Level " << (k + 1) << " is not coarser than level " << k; // ratio = prev/next; allow [0.5*levelRatio, 2*levelRatio] except at the // floor where we clamp to minCoarseVerts @@ -1038,23 +1039,23 @@ TEST(HLSCMInternal, BuildHierarchy_LevelCount) } } - // For each level: localToOriginal and originalToLocal are consistent inverses. + // For each level: local_to_original and original_to_local are consistent inverses. using Level = OpenABF::detail::hlscm::HierarchyLevel; auto isPresent = [](const Level& lvl, std::size_t origIdx) { - return origIdx < lvl.originalToLocal.size() && lvl.originalToLocal[origIdx].has_value(); + return origIdx < lvl.original_to_local.size() && lvl.original_to_local[origIdx].has_value(); }; for (std::size_t k = 0; k < levels.size(); ++k) { const auto& lvl = levels[k]; - EXPECT_EQ(lvl.localToOriginal.size(), lvl.positions.size()) - << "Level " << k << ": localToOriginal size != positions size"; - // originalToLocal is sized to the finest-mesh vertex count; check the - // round-trip via localToOriginal instead of the raw container size. - for (std::size_t li = 0; li < lvl.localToOriginal.size(); ++li) { - auto origIdx = lvl.localToOriginal[li]; - ASSERT_TRUE(isPresent(lvl, origIdx)) - << "Level " << k << ": original idx " << origIdx << " missing from originalToLocal"; - EXPECT_EQ(*lvl.originalToLocal[origIdx], li) - << "Level " << k << ": originalToLocal[" << origIdx << "] != " << li; + EXPECT_EQ(lvl.local_to_original.size(), lvl.positions.size()) + << "Level " << k << ": local_to_original size != positions size"; + // original_to_local is sized to the finest-mesh vertex count; check the + // round-trip via local_to_original instead of the raw container size. + for (std::size_t li = 0; li < lvl.local_to_original.size(); ++li) { + auto origIdx = lvl.local_to_original[li]; + ASSERT_TRUE(isPresent(lvl, origIdx)) << "Level " << k << ": original idx " << origIdx + << " missing from original_to_local"; + EXPECT_EQ(*lvl.original_to_local[origIdx], li) + << "Level " << k << ": original_to_local[" << origIdx << "] != " << li; } } @@ -1070,8 +1071,8 @@ TEST(HLSCMInternal, BuildHierarchy_LevelCount) TEST(HLSCMInternal, ProlongateUVs_BarycentricReconstruction) { // Build a 2-level hierarchy on a small grid, assign known UVs at the - // coarse level, and verify that prolongateUVs reconstructs the removed - // vertices' UVs as barycentric interpolations of the containingTri's UVs. + // coarse level, and verify that ProlongateUVs reconstructs the removed + // vertices' UVs as barycentric interpolations of the containing_tri's UVs. using HLSCM = OpenABF::HierarchicalLSCM; auto mesh = ConstructGrid(5, 5); @@ -1079,7 +1080,7 @@ TEST(HLSCMInternal, ProlongateUVs_BarycentricReconstruction) constexpr std::size_t pin1 = 4; // opposite corner of the top row // Force a 2-level hierarchy (25 verts → ~6 verts at ratio 4). - auto [levels, collapsesByLevel] = OpenABF::detail::hlscm::buildHierarchy( + auto [levels, collapsesByLevel] = OpenABF::detail::hlscm::BuildHierarchy( mesh, std::vector{pin0, pin1}, /*levelRatio=*/4, /*minCoarseVerts=*/5); ASSERT_GE(levels.size(), std::size_t(2)) << "Expected at least 2 hierarchy levels"; ASSERT_FALSE(collapsesByLevel.empty()) << "Expected at least one collapse record set"; @@ -1091,15 +1092,15 @@ TEST(HLSCMInternal, ProlongateUVs_BarycentricReconstruction) // Assign deterministic UVs to coarse-level vertices: U = origIdx, V = -origIdx // (anything works as long as we have one UV per surviving vertex) OpenABF::detail::hlscm::UVVector coarseUVs(mesh->num_vertices()); - for (auto origIdx : levels[1].localToOriginal) { + for (auto origIdx : levels[1].local_to_original) { coarseUVs[origIdx] = OpenABF::Vec(static_cast(origIdx), -static_cast(origIdx)); } - auto fineUVs = OpenABF::detail::hlscm::prolongateUVs(coarseUVs, collapses); + auto fineUVs = OpenABF::detail::hlscm::ProlongateUVs(coarseUVs, collapses); // Coarse UVs must survive untouched - for (auto origIdx : levels[1].localToOriginal) { + for (auto origIdx : levels[1].local_to_original) { const auto& uv = *coarseUVs[origIdx]; ASSERT_TRUE(fineUVs[origIdx].has_value()) << "Coarse vertex " << origIdx << " missing from prolongated UVs"; @@ -1109,34 +1110,34 @@ TEST(HLSCMInternal, ProlongateUVs_BarycentricReconstruction) << "Coarse vertex " << origIdx << " V mutated"; } - // Replay barycentric expansion in the same order as prolongateUVs (reverse + // Replay barycentric expansion in the same order as ProlongateUVs (reverse // of the collapse log) and verify the prolongated UVs match within 1e-5. auto expected = coarseUVs; for (auto it = collapses.rbegin(); it != collapses.rend(); ++it) { const auto& rec = *it; - ASSERT_TRUE(expected[rec.containingTri[0]].has_value()) - << "containingTri[0] " << rec.containingTri[0] << " UV missing"; - ASSERT_TRUE(expected[rec.containingTri[1]].has_value()) - << "containingTri[1] " << rec.containingTri[1] << " UV missing"; - ASSERT_TRUE(expected[rec.containingTri[2]].has_value()) - << "containingTri[2] " << rec.containingTri[2] << " UV missing"; - OpenABF::Vec exp = *expected[rec.containingTri[0]] * rec.bary[0] + - *expected[rec.containingTri[1]] * rec.bary[1] + - *expected[rec.containingTri[2]] * rec.bary[2]; - expected[rec.vRemoved] = exp; - - ASSERT_TRUE(fineUVs[rec.vRemoved].has_value()) - << "Removed vertex " << rec.vRemoved << " missing from prolongated UVs"; - EXPECT_NEAR((*fineUVs[rec.vRemoved])[0], exp[0], 1e-5f) - << "vertex " << rec.vRemoved << " U mismatch"; - EXPECT_NEAR((*fineUVs[rec.vRemoved])[1], exp[1], 1e-5f) - << "vertex " << rec.vRemoved << " V mismatch"; + ASSERT_TRUE(expected[rec.containing_tri[0]].has_value()) + << "containing_tri[0] " << rec.containing_tri[0] << " UV missing"; + ASSERT_TRUE(expected[rec.containing_tri[1]].has_value()) + << "containing_tri[1] " << rec.containing_tri[1] << " UV missing"; + ASSERT_TRUE(expected[rec.containing_tri[2]].has_value()) + << "containing_tri[2] " << rec.containing_tri[2] << " UV missing"; + OpenABF::Vec exp = *expected[rec.containing_tri[0]] * rec.bary[0] + + *expected[rec.containing_tri[1]] * rec.bary[1] + + *expected[rec.containing_tri[2]] * rec.bary[2]; + expected[rec.v_removed] = exp; + + ASSERT_TRUE(fineUVs[rec.v_removed].has_value()) + << "Removed vertex " << rec.v_removed << " missing from prolongated UVs"; + EXPECT_NEAR((*fineUVs[rec.v_removed])[0], exp[0], 1e-5f) + << "vertex " << rec.v_removed << " U mismatch"; + EXPECT_NEAR((*fineUVs[rec.v_removed])[1], exp[1], 1e-5f) + << "vertex " << rec.v_removed << " V mismatch"; } } TEST(HLSCMInternal, SolveLSCMLevel_KnownMesh) { - // Call detail::hlscm::solveLSCMLevel directly on a single-level pyramid + // Call detail::hlscm::SolveLSCMLevel directly on a single-level pyramid // HierarchyLevel and assert: // - all returned UVs are finite and z-component is implicit 0 // - pinned vertices have the prescribed UV positions (same pin @@ -1152,12 +1153,12 @@ TEST(HLSCMInternal, SolveLSCMLevel_KnownMesh) // Build a single-level hierarchy (pyramid is too small to decimate). auto meshH = ConstructPyramid(); - auto [levels, collapsesByLevel] = OpenABF::detail::hlscm::buildHierarchy( + auto [levels, collapsesByLevel] = OpenABF::detail::hlscm::BuildHierarchy( meshH, std::vector{pin0, pin1}, /*levelRatio=*/10, /*minCoarseVerts=*/100); ASSERT_EQ(levels.size(), std::size_t(1)) << "Pyramid should produce a single-level hierarchy"; const auto& level = levels[0]; - auto levelMesh = OpenABF::detail::hlscm::buildLevelMesh(level); + auto levelMesh = OpenABF::detail::hlscm::BuildLevelMesh(level); OpenABF::ComputeMeshAngles(levelMesh); const auto origVertCount = meshH->num_vertices(); @@ -1166,7 +1167,7 @@ TEST(HLSCMInternal, SolveLSCMLevel_KnownMesh) {pin0, OpenABF::Vec{0.f, 0.f}}, {pin1, OpenABF::Vec{2.f, 0.f}}, }; - auto uvs = OpenABF::detail::hlscm::solveLSCMLevel(levelMesh, level, pins, + auto uvs = OpenABF::detail::hlscm::SolveLSCMLevel(levelMesh, level, pins, origVertCount, nullptr); // Pyramid produces a single-level hierarchy, so every vertex must have a UV. @@ -1199,14 +1200,14 @@ TEST(HLSCMInternal, SolveLSCMLevel_KnownMesh) } // ------------------------------------------------------------------ -// LSCMSystemBuild — direct tests for detail::lscm::buildSystem (A8). +// LSCMSystemBuild — direct tests for detail::lscm::BuildSystem (A8). // -// buildSystem is the shared system-assembly utility extracted from -// AngleBasedLSCM::ComputeImpl and HierarchicalLSCM::solveLSCMLevel. These +// BuildSystem is the shared system-assembly utility extracted from +// AngleBasedLSCM::ComputeImpl and HierarchicalLSCM::SolveLSCMLevel. These // tests exercise it on a pyramid (3 faces, 4 vertices, 2 pins → 2 free), // asserting the structural invariants of the produced system rather than // the numerical solution (which is covered transitively by the existing -// parameterization tests once both call sites migrate to buildSystem). +// parameterization tests once both call sites migrate to BuildSystem). // ------------------------------------------------------------------ namespace { @@ -1227,7 +1228,7 @@ auto BuildPyramidSystem() {pin0Idx, Vec{0.f, 0.f}}, {pin1Idx, Vec{2.f, 0.f}}, }; - auto parts = OpenABF::detail::lscm::buildSystem(mesh, pins); + auto parts = OpenABF::detail::lscm::BuildSystem(mesh, pins); return std::make_tuple(mesh, p0, p1, std::move(parts)); } @@ -1280,7 +1281,7 @@ TEST(LSCMSystemBuild, PinRowsLandInB) { auto [mesh, p0, p1, parts] = BuildPyramidSystem(); - // After buildSystem, p0 sits at the UV origin and p1 sits on the axis + // After BuildSystem, p0 sits at the UV origin and p1 sits on the axis // whose component of (p1->pos - p0->pos) had the largest magnitude. // Therefore b = bFree * bFixed * -1 must have at least one nonzero entry // (the pin1 axis contributes a nonzero displacement into b). @@ -1314,7 +1315,7 @@ TEST(LSCMSystemBuild, FreeIdxTable_DeterministicAcrossRuns) {pin0Idx, Vec{0.f, 0.f}}, {pin1Idx, Vec{3.f, 0.f}}, }; - return OpenABF::detail::lscm::buildSystem(mesh, pins); + return OpenABF::detail::lscm::BuildSystem(mesh, pins); }; auto parts1 = runOnce(); @@ -1431,7 +1432,7 @@ TEST(HLSCM, MultiPin_Hemisphere) { // 289-vertex mesh: exercises the multi-level hierarchy path. Pin three // equator vertices at chosen UVs and verify each lands exactly. Pins - // must survive every decimation level (the isPinned_ flag protects them). + // must survive every decimation level (the is_pinned_ flag protects them). using HLSCM = HierarchicalLSCM; using PinMap = typename HLSCM::PinMap; @@ -1593,8 +1594,8 @@ TEST(HLSCM, PinMap_Rejects_OutOfRange) TEST(HLSCM, Instance_PinMap_Rejects_OutOfRange) { // Regression guard: HLSCM instance compute() must validate the PinMap - // before forwarding to ComputeImpl/buildHierarchy (which would otherwise - // out-of-bounds-write into DecimationMesh::isPinned_). + // before forwarding to ComputeImpl/BuildHierarchy (which would otherwise + // out-of-bounds-write into DecimationMesh::is_pinned_). using HLSCM = HierarchicalLSCM; using PinMap = typename HLSCM::PinMap; @@ -1623,7 +1624,7 @@ TEST(HLSCM, MultiPin_FourPins_Grid) {63u, Vec{1.f, 1.f}}, }; HLSCM hlscm; - hlscm.setMinCoarseVertices(10); // force at least one decimation level + hlscm.set_min_coarse_vertices(10); // force at least one decimation level hlscm.set_pins(pins); hlscm.compute(mesh); From cddce462f86e99e056339e41fffd1c80412b65b5 Mon Sep 17 00:00:00 2001 From: Seth Parker Date: Sun, 14 Jun 2026 21:59:27 -0400 Subject: [PATCH 2/2] fix: CI lint + single-header sync after rename pass - LSCMSystem.hpp AutoPlacePair signature: re-apply v18-preferred wrap. - Regenerate single_include after the late computeQuadrics_/buildEdges_ rename. --- include/OpenABF/detail/LSCMSystem.hpp | 4 ++-- single_include/OpenABF/OpenABF.hpp | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/OpenABF/detail/LSCMSystem.hpp b/include/OpenABF/detail/LSCMSystem.hpp index 3d06a86..cb6188a 100644 --- a/include/OpenABF/detail/LSCMSystem.hpp +++ b/include/OpenABF/detail/LSCMSystem.hpp @@ -68,8 +68,8 @@ void ValidatePins(const typename MeshType::Pointer& mesh, const PinMap& pins) * either index is out of range. */ template -auto AutoPlacePair(const typename MeshType::Pointer& mesh, std::size_t p0Idx, std::size_t p1Idx) - -> PinMap +auto AutoPlacePair(const typename MeshType::Pointer& mesh, std::size_t p0Idx, + std::size_t p1Idx) -> PinMap { const auto numVerts = mesh->num_vertices(); if (p0Idx >= numVerts || p1Idx >= numVerts) { diff --git a/single_include/OpenABF/OpenABF.hpp b/single_include/OpenABF/OpenABF.hpp index 122587b..0b5e88b 100644 --- a/single_include/OpenABF/OpenABF.hpp +++ b/single_include/OpenABF/OpenABF.hpp @@ -3161,8 +3161,8 @@ void ValidatePins(const typename MeshType::Pointer& mesh, const PinMap& pins) * either index is out of range. */ template -auto AutoPlacePair(const typename MeshType::Pointer& mesh, std::size_t p0Idx, std::size_t p1Idx) - -> PinMap +auto AutoPlacePair(const typename MeshType::Pointer& mesh, std::size_t p0Idx, + std::size_t p1Idx) -> PinMap { const auto numVerts = mesh->num_vertices(); if (p0Idx >= numVerts || p1Idx >= numVerts) { @@ -3792,8 +3792,8 @@ class DecimationMesh num_alive_verts_ = nv; num_alive_faces_ = nf; - computeQuadrics_(); - buildEdges_(); + compute_quadrics_(); + build_edges_(); } /** Get number of alive vertices */ @@ -4139,13 +4139,13 @@ class DecimationMesh /** Rebuild the edge list from alive faces and return it */ auto rebuild_and_get_edges() -> const std::vector>& { - buildEdges_(); + build_edges_(); return edges_; } private: /** Recompute per-vertex QEM quadrics from the live faces (Garland-Heckbert). */ - void computeQuadrics_() + void compute_quadrics_() { for (auto& q : quadrics_) { q = Quadric(); @@ -4181,7 +4181,7 @@ class DecimationMesh } /** Rebuild the unique-edge list from the live faces. */ - void buildEdges_() + void build_edges_() { edges_.clear(); std::unordered_set seen; @@ -4257,7 +4257,7 @@ class DecimationMesh std::size_t num_alive_verts_{0}; /** Count of alive faces (kept current across collapses). */ std::size_t num_alive_faces_{0}; - /** Unique-edge list, rebuilt on demand by `buildEdges_()`. */ + /** Unique-edge list, rebuilt on demand by `build_edges_()`. */ std::vector> edges_; /** Scratch buffer: neighbor indices shared between two endpoints. */