/// bench_boolean.cpp — 3D mesh boolean operation benchmarks /// /// Metrics: /// MeshBoolean_Union/N{faces} — sphere ∪ sphere /// MeshBoolean_Intersection/N{faces} — sphere ∩ sphere /// /// Two spheres (radius 1.0, separated by 0.5 along X) are generated via BrepModel, /// discretized to HalfedgeMesh with controlled segment counts, then passed to /// mesh_boolean(). #include #include #include #include #include #include using namespace vde; namespace { struct SpherePair { mesh::HalfedgeMesh a; mesh::HalfedgeMesh b; }; /// Create two intersecting spheres with approximately `face_count` faces each. /// sphere_a at origin, sphere_b translated by +1.0 along X (overlap ~0.5) SpherePair make_sphere_pair(int face_count) { // make_sphere produces ~ 2 * seg_u * seg_v faces → solve for seg int seg = std::max(static_cast(std::ceil(std::sqrt(face_count / 2.0))), 3); // Use deflection to control tessellation density per face // Each NURBS face tessellates res×res quads where res = max(4, 1/deflection) // For benchmark we want coarse tessellation: deflection = 0.5 → res=4 double deflection = 0.5; auto sphere_a = brep::make_sphere(1.0, seg, seg).to_mesh(deflection); auto sphere_b = brep::make_sphere(1.0, seg, seg).to_mesh(deflection); // Translate sphere_b by +1.0 along X so they overlap by ~0.5 // Rebuild from triangles with translated vertices mesh::HalfedgeMesh b_moved; std::vector all_verts; std::vector> all_tris; for (size_t fi = 0; fi < sphere_b.num_faces(); ++fi) { auto fv = sphere_b.face_vertices(static_cast(fi)); if (fv.size() < 3) continue; int base = static_cast(all_verts.size()); for (int vi : fv) { auto p = sphere_b.vertex(vi); all_verts.push_back(core::Point3D(p.x() + 1.0, p.y(), p.z())); } // triangulate if > 3 for (size_t j = 1; j + 1 < fv.size(); ++j) all_tris.push_back({base, base + static_cast(j), base + static_cast(j + 1)}); } b_moved.build_from_triangles(all_verts, all_tris); return {sphere_a, b_moved}; } } // namespace class MeshBooleanFixture : public benchmark::Fixture { public: void SetUp(const benchmark::State& state) override { auto pair = make_sphere_pair(state.range(0)); mesh_a = std::move(pair.a); mesh_b = std::move(pair.b); } mesh::HalfedgeMesh mesh_a; mesh::HalfedgeMesh mesh_b; }; BENCHMARK_DEFINE_F(MeshBooleanFixture, Union)(benchmark::State& state) { for (auto _ : state) { auto result = mesh::mesh_boolean(mesh_a, mesh_b, mesh::BooleanOp::Union); benchmark::DoNotOptimize(result.num_faces()); } state.SetItemsProcessed(state.iterations()); } BENCHMARK_REGISTER_F(MeshBooleanFixture, Union)->Arg(100)->Arg(500); BENCHMARK_DEFINE_F(MeshBooleanFixture, Intersection)(benchmark::State& state) { for (auto _ : state) { auto result = mesh::mesh_boolean(mesh_a, mesh_b, mesh::BooleanOp::Intersection); benchmark::DoNotOptimize(result.num_faces()); } state.SetItemsProcessed(state.iterations()); } BENCHMARK_REGISTER_F(MeshBooleanFixture, Intersection)->Arg(100)->Arg(500);