2026-07-24 07:04:55 +00:00
|
|
|
#include "vde/sdf/sdf_to_mesh.h"
|
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
|
|
|
|
|
using namespace vde::sdf;
|
2026-07-24 07:11:03 +00:00
|
|
|
using vde::core::Point3D;
|
2026-07-24 07:04:55 +00:00
|
|
|
|
|
|
|
|
TEST(SdfToMesh, SphereProducesValidMesh) {
|
|
|
|
|
auto sphere = SdfNode::sphere(2.0);
|
|
|
|
|
auto mesh = sdf_to_mesh(sphere, 32, 0.0);
|
|
|
|
|
|
|
|
|
|
EXPECT_GT(mesh.vertices.size(), 0u);
|
|
|
|
|
EXPECT_GT(mesh.triangles.size(), 0u);
|
|
|
|
|
|
|
|
|
|
// All vertices should be near the sphere surface (r ≈ 2)
|
|
|
|
|
for (const auto& v : mesh.vertices) {
|
|
|
|
|
double r = v.norm();
|
|
|
|
|
EXPECT_NEAR(r, 2.0, 1.0); // coarse resolution → loose tolerance
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Each triangle should have 3 indices
|
|
|
|
|
for (const auto& tri : mesh.triangles) {
|
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
|
|
|
EXPECT_GE(tri[i], 0);
|
|
|
|
|
EXPECT_LT(tri[i], static_cast<int>(mesh.vertices.size()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(SdfToMesh, UnionOfTwoSpheres) {
|
|
|
|
|
auto s1 = SdfNode::translate(SdfNode::sphere(1.0), Point3D(-1.5, 0, 0));
|
|
|
|
|
auto s2 = SdfNode::translate(SdfNode::sphere(1.0), Point3D(1.5, 0, 0));
|
|
|
|
|
auto tree = SdfNode::op_union(std::move(s1), std::move(s2));
|
|
|
|
|
|
|
|
|
|
auto mesh = sdf_to_mesh(tree, 32, 0.0);
|
|
|
|
|
|
|
|
|
|
EXPECT_GT(mesh.vertices.size(), 0u);
|
|
|
|
|
EXPECT_GT(mesh.triangles.size(), 0u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(SdfToMesh, BoxProducesValidMesh) {
|
|
|
|
|
auto box = SdfNode::box(Point3D(1, 1, 1));
|
|
|
|
|
auto mesh = sdf_to_mesh(box, 32, 0.0);
|
|
|
|
|
|
|
|
|
|
EXPECT_GT(mesh.vertices.size(), 0u);
|
|
|
|
|
EXPECT_GT(mesh.triangles.size(), 0u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(SdfToMesh, HigherResolutionProducesMoreTriangles) {
|
|
|
|
|
auto sphere = SdfNode::sphere(1.0);
|
|
|
|
|
auto low = sdf_to_mesh(sphere, 16, 0.0);
|
|
|
|
|
auto high = sdf_to_mesh(sphere, 32, 0.0);
|
|
|
|
|
|
|
|
|
|
EXPECT_GT(high.vertices.size(), low.vertices.size());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(SdfToMesh, LambdaSphere) {
|
|
|
|
|
auto mesh = sdf_to_mesh_lambda(
|
|
|
|
|
[](double x, double y, double z) {
|
|
|
|
|
return std::sqrt(x*x + y*y + z*z) - 2.0;
|
|
|
|
|
},
|
|
|
|
|
Point3D(-3, -3, -3), Point3D(3, 3, 3),
|
|
|
|
|
32, 0.0
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
EXPECT_GT(mesh.vertices.size(), 0u);
|
|
|
|
|
EXPECT_GT(mesh.triangles.size(), 0u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(SdfToMesh, LambdaBox) {
|
|
|
|
|
auto mesh = sdf_to_mesh_lambda(
|
|
|
|
|
[](double x, double y, double z) {
|
|
|
|
|
double dx = std::abs(x) - 1.0;
|
|
|
|
|
double dy = std::abs(y) - 1.0;
|
|
|
|
|
double dz = std::abs(z) - 1.0;
|
|
|
|
|
return std::sqrt(std::max(dx, 0.0) * std::max(dx, 0.0) +
|
|
|
|
|
std::max(dy, 0.0) * std::max(dy, 0.0) +
|
|
|
|
|
std::max(dz, 0.0) * std::max(dz, 0.0)) +
|
|
|
|
|
std::min(std::max({dx, dy, dz}), 0.0);
|
|
|
|
|
},
|
|
|
|
|
Point3D(-2, -2, -2), Point3D(2, 2, 2),
|
|
|
|
|
32, 0.0
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
EXPECT_GT(mesh.vertices.size(), 0u);
|
|
|
|
|
EXPECT_GT(mesh.triangles.size(), 0u);
|
|
|
|
|
}
|