Files
ViewDesignEngine/src/core/voronoi.cpp
T
ViewDesignEngine cf13496ff6
CI / Build & Test (push) Failing after 1m31s
CI / Release Build (push) Failing after 16m55s
feat: v0.5.0 P1 + 差异化功能 — Voronoi, Marching Cubes, 曲率, 序列化, Python绑定
新增功能:
- core: 2D Voronoi 图(Delaunay 对偶图)
- mesh: Marching Cubes(完整256项三角表 + SDF 辅助函数)
- mesh: 离散曲率(Gaussian/Mean, Cotan 公式)
- foundation: 二进制序列化(版本化格式,Little-Endian)
- python: pybind11 绑定(30+ API 暴露)
- marching_cubes.h: SDF 基本体(球/盒)+ 光滑布尔
2026-07-23 06:27:43 +00:00

102 lines
4.0 KiB
C++

#include "vde/core/voronoi.h"
#include "vde/mesh/delaunay_2d.h"
#include <unordered_map>
#include <algorithm>
namespace vde::core {
// Edge key for unordered_map
struct EdgeKey {
int a, b;
EdgeKey(int va, int vb) : a(std::min(va, vb)), b(std::max(va, vb)) {}
bool operator==(const EdgeKey& o) const { return a == o.a && b == o.b; }
};
struct EdgeKeyHash {
size_t operator()(const EdgeKey& e) const {
return (static_cast<uint64_t>(e.a) << 32) | static_cast<uint64_t>(e.b);
}
};
std::vector<VoronoiCell> voronoi_2d(const std::vector<Point2D>& points) {
std::vector<VoronoiCell> cells(points.size());
for (size_t i = 0; i < points.size(); ++i)
cells[i].site = points[i];
if (points.size() < 3) return cells;
// Compute Delaunay triangulation
auto dresult = mesh::delaunay_2d(points);
const auto& verts = dresult.vertices;
const auto& tris = dresult.triangles;
// Compute circumcenters for each Delaunay triangle
std::vector<Point2D> circumcenters;
for (const auto& tri : tris) {
Point2D a = verts[tri[0]], b = verts[tri[1]], c = verts[tri[2]];
double d = 2 * (a.x()*(b.y()-c.y()) + b.x()*(c.y()-a.y()) + c.x()*(a.y()-b.y()));
if (std::abs(d) < 1e-12) { circumcenters.push_back({0,0}); continue; }
double ux = ((a.x()*a.x()+a.y()*a.y())*(b.y()-c.y())
+ (b.x()*b.x()+b.y()*b.y())*(c.y()-a.y())
+ (c.x()*c.x()+c.y()*c.y())*(a.y()-b.y())) / d;
double uy = ((a.x()*a.x()+a.y()*a.y())*(c.x()-b.x())
+ (b.x()*b.x()+b.y()*b.y())*(a.x()-c.x())
+ (c.x()*c.x()+c.y()*c.y())*(b.x()-a.x())) / d;
circumcenters.push_back({ux, uy});
}
// For each vertex, collect circumcenters of adjacent triangles
for (size_t i = 0; i < points.size(); ++i) {
std::vector<Point2D> cell_verts;
std::unordered_map<EdgeKey, int, EdgeKeyHash> edge_first;
// Find all triangles containing this vertex
for (size_t ti = 0; ti < tris.size(); ++ti) {
const auto& tri = tris[ti];
for (int j = 0; j < 3; ++j) {
if (tri[j] == static_cast<int>(i) || tri[(j+1)%3] == static_cast<int>(i)) {
int v0 = tri[(j+0)%3], v1 = tri[(j+1)%3];
// Edge v0-v1 connects two circumcenters
// Find neighboring triangle sharing edge v0-v1
for (size_t tj = ti + 1; tj < tris.size(); ++tj) {
const auto& tri2 = tris[tj];
bool shares[3] = {false, false, false};
for (int k = 0; k < 3; ++k)
shares[k] = (tri2[k] == v0 || tri2[k] == v1 ||
tri2[k] == tri[(j+2)%3]);
}
}
}
}
// Simplified: just collect circumcenters of adjacent triangles
for (size_t ti = 0; ti < tris.size(); ++ti) {
const auto& tri = tris[ti];
bool contains = false;
for (int j = 0; j < 3; ++j)
if (tri[j] == static_cast<int>(i)) { contains = true; break; }
if (contains) {
cells[i].vertices.push_back(circumcenters[ti]);
}
}
// Sort circumcenters angularly around site for proper polygon
if (cells[i].vertices.size() >= 3) {
std::sort(cells[i].vertices.begin(), cells[i].vertices.end(),
[&](const Point2D& a, const Point2D& b) {
return std::atan2(a.y() - points[i].y(), a.x() - points[i].x()) <
std::atan2(b.y() - points[i].y(), b.x() - points[i].x());
});
// Deduplicate
auto last = std::unique(cells[i].vertices.begin(), cells[i].vertices.end(),
[](const Point2D& a, const Point2D& b) {
return (a-b).norm() < 1e-9;
});
cells[i].vertices.erase(last, cells[i].vertices.end());
}
}
return cells;
}
} // namespace vde::core