64ad721ed7
- CMake: INTERFACE 库修复、vde_compile_options 顺序修复 - 命名空间: 所有模块添加 using 声明 - 类型补全: Ray3Dd、Point4D - 头文件: tolerance 泛型化、std::optional include - 警告: 放宽 -Wconversion/-Wsign-conversion - 构建结果: 0 errors, 22/25 tests passed
99 lines
3.4 KiB
C++
99 lines
3.4 KiB
C++
#include "vde/mesh/mesh_boolean.h"
|
|
#include "vde/spatial/bvh.h"
|
|
#include "vde/core/line.h"
|
|
// Note: line.h already provides Ray3Dd
|
|
|
|
namespace vde::mesh {
|
|
|
|
using core::Triangle3D;
|
|
using core::Ray3Dd;
|
|
|
|
static bool is_point_inside(const Point3D& p, const HalfedgeMesh& mesh, const spatial::BVH& bvh) {
|
|
int hits = 0;
|
|
Vector3D dir(1, 0, 0);
|
|
Ray3Dd ray(p, dir);
|
|
auto results = bvh.query_ray(ray);
|
|
for (const auto& tri : results) {
|
|
Vector3D e1 = tri.v(1)-tri.v(0), e2 = tri.v(2)-tri.v(0);
|
|
Vector3D h = dir.cross(e2);
|
|
double a = e1.dot(h);
|
|
if (std::abs(a) < 1e-10) continue;
|
|
double f = 1.0/a;
|
|
Vector3D s = p - tri.v(0);
|
|
double u = f * s.dot(h);
|
|
if (u < 0 || u > 1) continue;
|
|
Vector3D q = s.cross(e1);
|
|
double v = f * dir.dot(q);
|
|
if (v < 0 || u+v > 1) continue;
|
|
double t = f * e2.dot(q);
|
|
if (t > 1e-10) hits++;
|
|
}
|
|
return (hits % 2) == 1;
|
|
}
|
|
|
|
HalfedgeMesh mesh_boolean(const HalfedgeMesh& a, const HalfedgeMesh& b, BooleanOp op) {
|
|
spatial::BVH bvh_a, bvh_b;
|
|
{
|
|
std::vector<Triangle3D> tris;
|
|
for (size_t fi = 0; fi < a.num_faces(); ++fi) {
|
|
auto vis = a.face_vertices(static_cast<int>(fi));
|
|
if (vis.size() == 3)
|
|
tris.emplace_back(a.vertex(vis[0]), a.vertex(vis[1]), a.vertex(vis[2]));
|
|
}
|
|
if (!tris.empty()) bvh_a.build(tris);
|
|
}
|
|
{
|
|
std::vector<Triangle3D> tris;
|
|
for (size_t fi = 0; fi < b.num_faces(); ++fi) {
|
|
auto vis = b.face_vertices(static_cast<int>(fi));
|
|
if (vis.size() == 3)
|
|
tris.emplace_back(b.vertex(vis[0]), b.vertex(vis[1]), b.vertex(vis[2]));
|
|
}
|
|
if (!tris.empty()) bvh_b.build(tris);
|
|
}
|
|
|
|
std::vector<Point3D> out_verts;
|
|
std::vector<std::array<int, 3>> out_tris;
|
|
|
|
auto add_tri = [&](const Point3D& v0, const Point3D& v1, const Point3D& v2) {
|
|
Vector3D n = (v1-v0).cross(v2-v0);
|
|
if (n.norm() < 1e-12) return;
|
|
int idx = static_cast<int>(out_verts.size());
|
|
out_verts.push_back(v0); out_verts.push_back(v1); out_verts.push_back(v2);
|
|
out_tris.push_back({idx, idx+1, idx+2});
|
|
};
|
|
|
|
auto class_for = [](bool in_a, bool in_b, BooleanOp o) -> bool {
|
|
switch (o) {
|
|
case BooleanOp::Union: return !in_a && !in_b;
|
|
case BooleanOp::Intersection: return in_a && in_b;
|
|
case BooleanOp::Difference: return in_a && !in_b;
|
|
default: return false;
|
|
}
|
|
};
|
|
|
|
for (size_t fi = 0; fi < a.num_faces(); ++fi) {
|
|
auto vis = a.face_vertices(static_cast<int>(fi));
|
|
if (vis.size() != 3) continue;
|
|
Point3D v0 = a.vertex(vis[0]), v1 = a.vertex(vis[1]), v2 = a.vertex(vis[2]);
|
|
Point3D c = (v0+v1+v2) / 3.0;
|
|
if (class_for(true, is_point_inside(c, b, bvh_b), op))
|
|
add_tri(v0, v1, v2);
|
|
}
|
|
for (size_t fi = 0; fi < b.num_faces(); ++fi) {
|
|
auto vis = b.face_vertices(static_cast<int>(fi));
|
|
if (vis.size() != 3) continue;
|
|
Point3D v0 = b.vertex(vis[0]), v1 = b.vertex(vis[1]), v2 = b.vertex(vis[2]);
|
|
Point3D c = (v0+v1+v2) / 3.0;
|
|
if (class_for(false, is_point_inside(c, a, bvh_a), op))
|
|
add_tri(v0, v1, v2);
|
|
}
|
|
|
|
HalfedgeMesh result;
|
|
if (!out_verts.empty())
|
|
result.build_from_triangles(out_verts, out_tris);
|
|
return result;
|
|
}
|
|
|
|
} // namespace vde::mesh
|