93 lines
3.3 KiB
C++
93 lines
3.3 KiB
C++
/// 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 <benchmark/benchmark.h>
|
||
#include <vde/brep/brep.h>
|
||
#include <vde/brep/modeling.h>
|
||
#include <vde/mesh/mesh_boolean.h>
|
||
#include <vde/mesh/halfedge_mesh.h>
|
||
#include <cmath>
|
||
|
||
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<int>(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<core::Point3D> all_verts;
|
||
std::vector<std::array<int, 3>> all_tris;
|
||
for (size_t fi = 0; fi < sphere_b.num_faces(); ++fi) {
|
||
auto fv = sphere_b.face_vertices(static_cast<int>(fi));
|
||
if (fv.size() < 3) continue;
|
||
int base = static_cast<int>(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<int>(j), base + static_cast<int>(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);
|