Kelsidavis-WoWee/tools/editor/cli_weld.hpp
Kelsi 3cf3b35885 refactor(editor): extract edge classification into cli_weld
The "loop over triangles, key edges by canonical-vertex pair,
count uses, classify boundary/manifold/non-manifold" pass was
duplicated across cli_mesh_info, cli_world_info, and the new
cli_audits watertight check. Hoist it into cli_weld as
classifyEdges(indices, canon) returning an EdgeStats struct
with boundary / manifold / nonManifold counters and a
watertight() convenience method.

All three callers verified byte-identical:
  • --info-mesh-stats firepit:    180 edges, watertight YES
  • --info-wob-stats cube:        18 manifold, watertight YES
  • --audit-watertight /tmp/...:  61 meshes, 12 failures, rc=12

About 60 more lines of duplication removed; classifyEdges +
buildWeldMap together form the complete reusable surface for
new weld/topology audit commands.
2026-05-09 11:15:31 -07:00

51 lines
1.9 KiB
C++

#pragma once
#include <glm/glm.hpp>
#include <cstddef>
#include <cstdint>
#include <vector>
namespace wowee {
namespace editor {
namespace cli {
// Vertex weld pass shared by --info-mesh-stats / --info-wob-stats /
// --bake-wom-collision / --audit-watertight. Positions are quantized
// onto a 1/eps grid; every vertex sharing a cell with a previously-
// seen vertex is remapped to that vertex's index. Returns canon[v]
// giving the canonical (lowest-index) representative of v's
// equivalence class and writes the count of distinct cells to
// `uniqueOut`.
//
// Implementation uses std::map<tuple<int64,int64,int64>, uint32_t>
// for exact equality on the quantized key — a hash-based key would
// risk false-positive collisions that incorrectly merge distinct
// corners (e.g. a unit cube's 8 corners all hashing to 2 buckets).
std::vector<uint32_t> buildWeldMap(
const std::vector<glm::vec3>& positions,
float eps,
std::size_t& uniqueOut);
// Edge classification result from walking a triangle list with a
// canon[] map (typically built by buildWeldMap above, but the
// identity mapping also works for "as-authored" edge counts).
struct EdgeStats {
std::size_t total = 0; // distinct edges seen
std::size_t boundary = 0; // shared by exactly 1 triangle (open seam)
std::size_t manifold = 0; // shared by exactly 2 (closed surface)
std::size_t nonManifold = 0; // shared by 3+ (branching surface)
bool watertight() const {
return boundary == 0 && nonManifold == 0;
}
};
// Walk every triangle in `indices` (must be a multiple of 3),
// remap each corner through canon[], and count edge uses. An
// edge whose two canonical endpoints are equal is dropped (it
// became a self-loop after welding and isn't a real edge).
EdgeStats classifyEdges(const std::vector<uint32_t>& indices,
const std::vector<uint32_t>& canon);
} // namespace cli
} // namespace editor
} // namespace wowee