feat(sdf): S11 — SDF 隐式建模完整模块
CI / Build & Test (push) Failing after 29s
CI / Release Build (push) Failing after 35s

S11-A: SDF 图元库
- 14 种基础形状(inline header)
- sphere/box/round_box/torus/capsule/cylinder/cone/plane/ellipsoid
- triangular_prism/hex_prism/link/wedge
- 2D 挤出(extrusion/extrusion_bounded/revolution)

S11-B: SDF 操作 + 域变形
- 锐利布尔: union/intersection/difference
- 平滑布尔: smooth_union/smooth_intersection/smooth_difference
- 修饰器: round/onion
- 域变形: repeat/mirror/rotate/translate/scale/twist/bend/elongate/displace/cheap_bend
- 24 项测试

S11-C: CSG 表达式树 + 网格转换 + Python 绑定
- SdfNode 树结构 — 工厂构造函数,递归求值
- sdf_to_mesh — 基于 marching_cubes 的 SDF→网格
- bind_sdf — pybind11 Python 绑定

文件: 14 文件,2,545 行(测试 1,296 行)
This commit is contained in:
茂之钳
2026-07-24 07:04:55 +00:00
parent 6d3f8fc8b3
commit b7419a7881
17 changed files with 1997 additions and 115 deletions
+1
View File
@@ -15,6 +15,7 @@ pybind11_add_module(_vde
src/bind_core.cpp
src/bind_curves.cpp
src/bind_mesh.cpp
src/bind_sdf.cpp
)
target_include_directories(_vde
+2
View File
@@ -8,6 +8,7 @@ namespace py = pybind11;
void bind_core(py::module& m);
void bind_curves(py::module& m);
void bind_mesh(py::module& m);
void bind_sdf(py::module& m);
PYBIND11_MODULE(_vde, m) {
m.doc() = "ViewDesignEngine — CAD computational geometry engine";
@@ -15,4 +16,5 @@ PYBIND11_MODULE(_vde, m) {
bind_core(m);
bind_curves(m);
bind_mesh(m);
bind_sdf(m);
}
+151
View File
@@ -0,0 +1,151 @@
#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>
#include "vde/sdf/sdf_tree.h"
#include "vde/sdf/sdf_to_mesh.h"
namespace py = pybind11;
using namespace vde::sdf;
void bind_sdf(py::module& m) {
auto sdf_m = m.def_submodule("sdf",
"Signed Distance Field implicit modeling");
// ── SdfNode ────────────────────────────────────────
py::class_<SdfNode, std::shared_ptr<SdfNode>>(sdf_m, "SdfNode",
R"pbdoc(
A node in a signed-distance-field CSG expression tree.
SdfNode represents either a primitive shape, a boolean CSG operation,
a distance modifier, or a domain transform. Build trees by combining
nodes with the factory functions.
)pbdoc")
// Primitive factories
.def_static("sphere", &SdfNode::sphere,
py::arg("radius"), "Sphere centered at origin")
.def_static("box", &SdfNode::box,
py::arg("half_extents"), "Axis-aligned box")
.def_static("round_box", &SdfNode::round_box,
py::arg("half_extents"), py::arg("radius"), "Rounded box")
.def_static("cylinder", &SdfNode::cylinder,
py::arg("radius"), py::arg("height"), "Capped cylinder along Y")
.def_static("torus", &SdfNode::torus,
py::arg("major_radius"), py::arg("minor_radius"), "Torus in XZ plane")
.def_static("capsule", &SdfNode::capsule,
py::arg("a"), py::arg("b"), py::arg("radius"), "Capsule from a to b")
.def_static("cone", &SdfNode::cone,
py::arg("angle_rad"), py::arg("height"), "Cone along Y axis")
.def_static("plane", &SdfNode::plane,
py::arg("normal"), py::arg("offset"), "Infinite plane")
.def_static("ellipsoid", &SdfNode::ellipsoid,
py::arg("radii"), "Ellipsoid centered at origin")
.def_static("triangular_prism", &SdfNode::triangular_prism,
py::arg("height"), "Triangular prism along Y")
.def_static("hex_prism", &SdfNode::hex_prism,
py::arg("height"), "Hexagonal prism along Y")
.def_static("link", &SdfNode::link,
py::arg("radius"), py::arg("length"), py::arg("thickness"), "Link segment")
.def_static("wedge", &SdfNode::wedge,
py::arg("extents"), "Wedge shape")
// Boolean CSG operations
.def_static("union_", &SdfNode::op_union,
py::arg("a"), py::arg("b"), "CSG union (min)")
.def_static("intersection", &SdfNode::op_intersection,
py::arg("a"), py::arg("b"), "CSG intersection (max)")
.def_static("difference", &SdfNode::op_difference,
py::arg("a"), py::arg("b"), "CSG difference (max(a, -b))")
.def_static("smooth_union", &SdfNode::smooth_union,
py::arg("a"), py::arg("b"), py::arg("k"), "Smooth blend union")
.def_static("smooth_intersection", &SdfNode::smooth_intersection,
py::arg("a"), py::arg("b"), py::arg("k"), "Smooth blend intersection")
.def_static("smooth_difference", &SdfNode::smooth_difference,
py::arg("a"), py::arg("b"), py::arg("k"), "Smooth blend difference")
// Modifiers
.def_static("round", &SdfNode::round,
py::arg("child"), py::arg("radius"), "Offset surface outward")
.def_static("onion", &SdfNode::onion,
py::arg("child"), py::arg("thickness"), "Hollow shell")
// Domain transforms
.def_static("repeat", &SdfNode::repeat,
py::arg("child"), py::arg("cell"), "Infinite repetition")
.def_static("mirror_x", &SdfNode::mirror_x,
py::arg("child"), "Mirror across YZ plane")
.def_static("mirror_y", &SdfNode::mirror_y,
py::arg("child"), "Mirror across XZ plane")
.def_static("mirror_z", &SdfNode::mirror_z,
py::arg("child"), "Mirror across XY plane")
.def_static("translate", &SdfNode::translate,
py::arg("child"), py::arg("offset"), "Translate in space")
.def_static("rotate", &SdfNode::rotate,
py::arg("child"), py::arg("angle_rad"), "Rotate around Y axis")
.def_static("scale", &SdfNode::scale,
py::arg("child"), py::arg("factors"), "Non-uniform scale")
.def_static("twist", &SdfNode::twist,
py::arg("child"), py::arg("amount"), "Twist around Y axis")
.def_static("bend", &SdfNode::bend,
py::arg("child"), py::arg("amount"), "Bend around Z axis")
.def_static("elongate", &SdfNode::elongate,
py::arg("child"), py::arg("h"), "Elongate (stretch space)")
.def_static("cheap_bend", &SdfNode::cheap_bend,
py::arg("child"), py::arg("amount"), "Simplified bend")
.def_static("displace", &SdfNode::displace,
py::arg("child"), py::arg("amplitude"), py::arg("frequency"),
"Sinusoidal displacement")
// Accessors
.def_readwrite("name", &SdfNode::name, "Optional label")
.def("__repr__", [](const SdfNode& n) {
return "SdfNode(" + n.name + ")";
});
// ── evaluate() ─────────────────────────────────────
sdf_m.def("evaluate", &evaluate,
py::arg("node"), py::arg("point"),
"Evaluate SDF tree at point. Returns signed distance.");
sdf_m.def("estimate_bounds", &estimate_bounds,
py::arg("root"), py::arg("margin") = 1.0,
"Estimate half-extent bounding radius of SDF tree");
// ── SdfBBox ──────────────────────────────────────
py::class_<SdfBBox>(sdf_m, "SdfBBox")
.def(py::init<>())
.def_readwrite("min", &SdfBBox::min)
.def_readwrite("max", &SdfBBox::max);
sdf_m.def("estimate_bbox", &estimate_bbox,
py::arg("root"), py::arg("margin") = 1.0,
"Estimate full AABB of SDF tree");
// ── sdf_to_mesh() ──────────────────────────────────
py::class_<vde::mesh::MCMesh>(sdf_m, "MCMesh",
R"pbdoc(
Marching cubes mesh result.
Attributes:
vertices: N x 3 list of Point3D
triangles: M x 3 list of index triples
)pbdoc")
.def(py::init<>())
.def_readwrite("vertices", &vde::mesh::MCMesh::vertices)
.def_readwrite("triangles", &vde::mesh::MCMesh::triangles)
.def("__repr__", [](const vde::mesh::MCMesh& m) {
return "MCMesh(v=" + std::to_string(m.vertices.size())
+ ", t=" + std::to_string(m.triangles.size()) + ")";
});
sdf_m.def("sdf_to_mesh", &sdf_to_mesh,
py::arg("root"), py::arg("resolution") = 64,
py::arg("iso_level") = 0.0,
"Convert SDF tree to triangle mesh via marching cubes");
sdf_m.def("sdf_to_mesh_lambda", &sdf_to_mesh_lambda,
py::arg("f"), py::arg("bmin"), py::arg("bmax"),
py::arg("resolution") = 64, py::arg("iso_level") = 0.0,
"Convert lambda SDF f(x,y,z) to triangle mesh");
}
+5 -4
View File
@@ -2,11 +2,12 @@
ViewDesignEngine — CAD computational geometry engine for Python.
Submodules:
vde.core — Point3D, Vector3D, AABB3D, Polygon2D, convex hull, distance, transforms
vde.core — Point3D, Vector3D, AABB3D, Polygon2D, convex hull, distance, transforms
vde.curves — BezierCurve, BSplineCurve, NurbsCurve
vde.mesh — delaunay_2d, delaunay_3d, HalfedgeMesh
vde.mesh — delaunay_2d, delaunay_3d, HalfedgeMesh
vde.sdf — SdfNode, CSG tree, marching cubes mesh conversion
"""
from ._vde import core, curves, mesh
from ._vde import core, curves, mesh, sdf
__version__ = "1.0.0"
__all__ = ["core", "curves", "mesh"]
__all__ = ["core", "curves", "mesh", "sdf"]