// ============================================================================
// dc_pocket GATE — docs/level-mesh-implementation.md §12 step 5.
//
// The doc's acceptance criterion is exactly: "carve a wall, pocket appears, undo
// restores exact geometry."
//
// PART 1 of this file is a CONTROL that reproduces the bug as it ships today.
// It is not decoration. Before this module existed, DCBrushStore::csg4_view
// skipped every carved brush (dc_brush_store.h:227) and nothing meshed what was
// left, so a carve did not carve — it DELETED the wall. The control asserts that
// the exact mesher loses the geometry, which is what makes PART 2's pocket the
// thing that puts it back rather than an unfalsifiable "looks fine".
//
// PART 3 is the reversibility half, and it is the one that matters most for the
// architecture: the whole design rests on "a carve NEVER mutates a brush"
// (dc_brush_store.h:16). That is checked with the store's own fingerprint, so it
// is a bit-level claim, not an eyeball.
//
// Build:
// g++ -O2 -std=c++17 -I libdc/include -o /tmp/pocket \
// tests/test_pocket.cpp libdc/src/dc_csg4.cpp libdc/src/dc_level_mesh.cpp \
// libdc/src/dc_mesher.cpp libdc/src/dc_contour.cpp libdc/src/dc_octree.cpp \
// libdc/src/dc_qef.cpp libdc/src/dc_density.cpp libdc/src/dc_simplify.cpp
// ============================================================================
#include "dc_brush_store.h"
#include "dc_csg4.h"
#include "dc_level_mesh.h"
#include "dc_pocket.h"
#include <cstdio>
#include <cmath>
#include <string>
#include <vector>
static int failures = 0;
static void check(bool cond, const char* what, const char* detail = "") {
printf(" %s %s %s\n", cond ? "ok " : "FAIL", what, detail);
if (!cond) failures++;
}
// An axis-aligned slab: the simplest thing that reads as "a wall". 6 half-spaces,
// inside = n·p <= d.
static uint32_t add_box_brush(DCBrushStore& s, float cx, float cy, float cz,
float hx, float hy, float hz, uint32_t mat = 1) {
std::vector<ConvexPlane> p = {
{ 1, 0, 0, cx + hx}, {-1, 0, 0, -(cx - hx)},
{ 0, 1, 0, cy + hy}, { 0,-1, 0, -(cy - hy)},
{ 0, 0, 1, cz + hz}, { 0, 0,-1, -(cz - hz)},
};
std::vector<uint32_t> proj(p.size(), 0u);
return s.add_brush(p, proj, mat);
}
static int level_triangles(const DCBrushStore& s) {
std::vector<CSG4Brush> view;
s.csg4_view(view);
std::vector<CSG4Face> faces;
dc_csg4::clip_all(view, faces, nullptr);
DCLevelMeshData m;
dc_level_mesh::build(faces, m);
return (int)m.triangle_count();
}
int main() {
printf("=== dc_pocket gate (level-mesh-implementation.md §12 step 5) ===\n");
DCBrushStore store;
// A wall 4m wide, 3m tall, 0.5m thick — thick enough that a 0.6m carve does
// not punch straight through, so the pocket has to represent a DENT, which is
// a harder and more honest case than a hole.
const uint32_t wall = add_box_brush(store, 0, 0, 0, 2.0f, 1.5f, 0.25f);
const uint64_t fp_before = store.fingerprint();
const int tris_before = level_triangles(store);
printf("\n-- baseline ---------------------------------------------------\n");
check(tris_before > 0, "the wall meshes as exact geometry",
(std::to_string(tris_before) + " tris").c_str());
printf("\n-- PART 1: the bug this module exists to fix -------------------\n");
TerrainEdit carve;
carve.shape = TerrainEdit::SPHERE;
carve.op = TerrainEdit::OP_HARD;
carve.x = 0.0f; carve.y = 0.0f; carve.z = 0.0f;
carve.radius = 0.6f;
carve.strength = -1.0f;
const uint32_t carve_id = store.apply_carve(carve);
const int tris_carved = level_triangles(store);
check(tris_carved == 0, "carving REMOVES the brush from the exact mesh",
(std::to_string(tris_carved) + " tris left").c_str());
printf(" ^ this is the defect: without a pocket the wall is GONE,\n");
printf(" not carved. Everything below is what puts it back.\n");
printf("\n-- PART 2: the pocket supplies the missing geometry ------------\n");
std::vector<std::vector<uint32_t>> comps;
store.pockets(comps);
check(comps.size() == 1, "one connected component of carved brushes",
(std::to_string(comps.size()) + " components").c_str());
dc_pocket::PocketConfig cfg;
cfg.voxel_size = 0.05f; // fine enough to resolve a 0.5m-thick wall
dc_pocket::PocketResult pocket;
const bool built = comps.size() == 1 &&
dc_pocket::build(store, comps[0], cfg, pocket);
check(built, "pocket builds");
const int ptris = built ? (int)(pocket.mesh.indices.size() / 3) : 0;
check(ptris > 0, "pocket has geometry",
(std::to_string(ptris) + " tris").c_str());
// Containment: a pocket is a derived cache of (brush - carves), so it cannot
// extend beyond the brush it came from. This is what makes pockets meet the
// surrounding exact mesh at a brush face (docs §8, "crack-free by
// construction") — if it leaks, that claim is void.
if (built && ptris > 0) {
float lo[3] = { 1e30f, 1e30f, 1e30f};
float hi[3] = {-1e30f, -1e30f, -1e30f};
for (size_t i = 0; i + 2 < pocket.mesh.positions.size(); i += 3)
for (int a = 0; a < 3; a++) {
lo[a] = std::min(lo[a], pocket.mesh.positions[i + a]);
hi[a] = std::max(hi[a], pocket.mesh.positions[i + a]);
}
const DCBrush* b = store.get(wall);
// One voxel of slack: DC places vertices by QEF inside boundary cells, so
// the surface can sit a fraction of a cell outside the exact plane.
const float slack = cfg.voxel_size * 1.5f;
const bool inside =
lo[0] >= b->lo.x - slack && hi[0] <= b->hi.x + slack &&
lo[1] >= b->lo.y - slack && hi[1] <= b->hi.y + slack &&
lo[2] >= b->lo.z - slack && hi[2] <= b->hi.z + slack;
char d[160];
snprintf(d, sizeof(d), "pocket [%.2f,%.2f,%.2f]..[%.2f,%.2f,%.2f] vs brush [%.2f,%.2f,%.2f]..[%.2f,%.2f,%.2f]",
lo[0], lo[1], lo[2], hi[0], hi[1], hi[2],
b->lo.x, b->lo.y, b->lo.z, b->hi.x, b->hi.y, b->hi.z);
check(inside, "pocket stays within its brush (seam anchor holds)", d);
// The carve must actually be visible in the result. A pocket that meshed
// the UNCARVED brush would pass every check above — this is the one that
// says the sphere was subtracted at all.
bool any_near_carve = false;
for (size_t i = 0; i + 2 < pocket.mesh.positions.size(); i += 3) {
const float dx = pocket.mesh.positions[i + 0] - carve.x;
const float dy = pocket.mesh.positions[i + 1] - carve.y;
const float dz = pocket.mesh.positions[i + 2] - carve.z;
const float r = std::sqrt(dx*dx + dy*dy + dz*dz);
if (std::fabs(r - carve.radius) < cfg.voxel_size * 3.0f) { any_near_carve = true; break; }
}
check(any_near_carve, "the carve surface is present in the pocket mesh");
}
printf("\n-- PART 3: undo restores EXACT geometry ------------------------\n");
check(store.undo_carve(carve_id), "carve undone");
check(store.fingerprint() == fp_before,
"brush set is BIT-IDENTICAL after carve+undo (a carve never mutates a brush)");
std::vector<std::vector<uint32_t>> comps_after;
store.pockets(comps_after);
check(comps_after.empty(), "no pockets remain");
const int tris_after = level_triangles(store);
check(tris_after == tris_before, "exact mesh is restored",
(std::to_string(tris_after) + " vs " + std::to_string(tris_before) + " tris").c_str());
printf("\n=== %s ===\n", failures == 0 ? "PASS" : "FAIL");
return failures == 0 ? 0 : 1;
}