Files
ViewDesignEngine/include/vde/sdf/sdf_tree.h
T
茂之钳 4c9ee4f760
CI / Build & Test (push) Failing after 29s
CI / Release Build (push) Failing after 38s
docs: doxygen annotations for curves + mesh + sketch
2026-07-24 11:04:04 +00:00

360 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
/**
* @file sdf_tree.h
* @brief SDF 表达式树结构
*
* 将 SDF 原语和 CSG 操作以树形数据结构表示,支持树遍历、求值和包围盒估算。
*
* ## 架构层级
*
* ```
* SdfNode (树节点) — 单节点,含操作类型+参数+子节点
* ├─ enum SdfOp — 操作枚举(图元/CSG/修饰/变形)
* ├─ struct SdfParams — 参数联合体
* ├─ 静态工厂方法 — 创建各类型节点
* └─ visit() — 前序遍历
*
* evaluate() — 递归求值
* estimate_bounds() / estimate_bbox() — 包围盒估算
* ```
*
* ## 使用模式
*
* 通过静态工厂方法构建节点树,然后调用 evaluate() 进行求值:
*
* @code{.cpp}
* auto root = SdfNode::op_union(
* SdfNode::sphere(1.0),
* SdfNode::box(Point3D(0.5, 0.5, 0.5))
* );
* double d = evaluate(root, Point3D(1.0, 0.0, 0.0));
* @endcode
*
* @ingroup sdf
*/
#include "vde/sdf/sdf_primitives.h"
#include "vde/sdf/sdf_operations.h"
#include "vde/core/point.h"
#include <memory>
#include <string>
#include <vector>
namespace vde::sdf {
using core::Point3D;
using core::Vector3D;
// Forward declaration
class SdfNode;
using SdfNodePtr = std::shared_ptr<SdfNode>;
/**
* @brief CSG 树的所有节点类型
*
* 枚举涵盖三类操作:
* - **图元**Sphere, Box, ... — 叶子节点,产生基本几何体
* - **CSG 布尔**Union, Intersection, Difference — 二叉树组合
* - **修饰/变形**Round, Translate, Twist, ... — 单子节点变换
*
* @see SdfParams 对应的参数结构
*/
enum class SdfOp : uint8_t {
// Primitives
Sphere, Box, RoundBox, Torus, Capsule, Cylinder, Cone, Plane,
Ellipsoid, TriangularPrism, HexPrism, Link, Wedge,
// Boolean CSG
Union, Intersection, Difference,
SmoothUnion, SmoothIntersection, SmoothDifference,
// Modifiers
Round, Onion,
// Domain transforms
Repeat, MirrorX, MirrorY, MirrorZ, Translate, Rotate, Scale,
Twist, Bend, Elongate, CheapBend,
// Displacement
Displace
};
/**
* @brief SDF 节点参数联合体
*
* 一个结构体囊括所有可能的参数。不同操作类型使用不同字段子集:
*
* | 操作类型 | 使用字段 |
* |-------------------|--------------------------------------------|
* | Sphere | radius |
* | Box / RoundBox | extents |
* | Cylinder / Cone | radius, height, angle_rad |
* | Plane | normal, offset |
* | Torus | major_radius, minor_radius |
* | Capsule | pt_a, pt_b, radius |
* | Smooth CSG | blend_k |
* | Repeat | repeat_cell |
* | Translate | translate_offset |
* | Scale | scale_factors |
* | Displace | amplitude, frequency |
* | Twist/Bend | amount |
* | Onion | thickness |
* | Round | offset |
*
* @warning 不使用的字段保持默认值即可,但不要依赖未使用字段的默认值来做逻辑判断。
*/
struct SdfParams {
// ── Primitive parameters ──
double radius = 1.0; ///< 球体/圆柱/胶囊/圆角半径
Point3D extents = Point3D(1, 1, 1); ///< 盒子的半边长
Point3D pt_a = Point3D(0, -1, 0); ///< 胶囊端点 A
Point3D pt_b = Point3D(0, 1, 0); ///< 胶囊端点 B
double major_radius = 1.0; ///< 圆环主半径
double minor_radius = 0.3; ///< 圆环副半径
double angle_rad = 0.5; ///< 锥体半角(弧度)
double height = 2.0; ///< 柱体/锥体/挤出高度
double thickness = 0.1; ///< 壳层厚度(Onion
Vector3D normal = Vector3D(0, 1, 0); ///< 平面法向量
double offset = 0.0; ///< 平面偏移 / 通用偏移
// ── Operation parameters ──
double blend_k = 0.2; ///< 平滑 CSG 混合参数
double amount = 0.5; ///< 扭曲/弯曲量
double amplitude = 0.1; ///< 位移扰动幅值
double frequency = 1.0; ///< 位移扰动频率
Point3D repeat_cell = Point3D(2, 2, 2); ///< 重复晶格尺寸
Point3D translate_offset = Point3D(0, 0, 0); ///< 平移量
Point3D scale_factors = Point3D(1, 1, 1); ///< 缩放因子
};
/**
* @brief SDF 表达式树节点
*
* 每个节点包含:
* - `op`: 操作类型
* - `params`: 参数值
* - `children`: 子节点列表(CSG 布尔 = 2 个子节点,修饰/变形 = 1 个,图元 = 0 个)
* - `name`: 可选名称(调试用)
*
* 使用 make_shared<SdfNode>(op) 或静态工厂方法创建节点。
* 所有工厂方法返回 SdfNodePtrshared_ptr),自动管理生命周期。
*
* @note 树是不可变的——节点创建后 op 和子节点关系不变,但 params 可被外部修改以支持参数优化。
*/
class SdfNode {
public:
SdfOp op; ///< 操作类型
SdfParams params; ///< 参数值
std::vector<SdfNodePtr> children; ///< 子节点
std::string name; ///< 节点名(调试用)
// ── Primitive factories ──
/** @brief 创建球体节点 */
static SdfNodePtr sphere(double r);
/** @brief 创建轴对齐长方体节点 */
static SdfNodePtr box(const Point3D& half_extents);
/** @brief 创建圆角长方体节点 */
static SdfNodePtr round_box(const Point3D& half_extents, double r);
/** @brief 创建圆柱体节点 */
static SdfNodePtr cylinder(double r, double h);
/** @brief 创建圆环体节点 */
static SdfNodePtr torus(double major_r, double minor_r);
/** @brief 创建胶囊体节点 */
static SdfNodePtr capsule(const Point3D& a, const Point3D& b, double r);
/** @brief 创建圆锥台节点 */
static SdfNodePtr cone(double angle_rad, double h);
/** @brief 创建平面节点 */
static SdfNodePtr plane(const Vector3D& normal, double offset);
/** @brief 创建椭球体节点 */
static SdfNodePtr ellipsoid(const Point3D& radii);
/** @brief 创建三角棱柱节点 */
static SdfNodePtr triangular_prism(double h);
/** @brief 创建六棱柱节点 */
static SdfNodePtr hex_prism(double h);
/** @brief 创建双环链接节点 */
static SdfNodePtr link(double r, double length, double thickness);
/** @brief 创建楔形体节点 */
static SdfNodePtr wedge(const Point3D& extents);
// ── Boolean CSG factories (two children) ──
/** @brief 创建并集节点: A B */
static SdfNodePtr op_union(SdfNodePtr a, SdfNodePtr b);
/** @brief 创建交集节点: A ∩ B */
static SdfNodePtr op_intersection(SdfNodePtr a, SdfNodePtr b);
/** @brief 创建差集节点: A \ B */
static SdfNodePtr op_difference(SdfNodePtr a, SdfNodePtr b);
/** @brief 创建平滑并集节点 */
static SdfNodePtr smooth_union(SdfNodePtr a, SdfNodePtr b, double k);
/** @brief 创建平滑交集节点 */
static SdfNodePtr smooth_intersection(SdfNodePtr a, SdfNodePtr b, double k);
/** @brief 创建平滑差集节点 */
static SdfNodePtr smooth_difference(SdfNodePtr a, SdfNodePtr b, double k);
// ── Modifier factories (one child) ──
/** @brief 创建圆角偏移节点 */
static SdfNodePtr round(SdfNodePtr child, double r);
/** @brief 创建壳层修饰节点 */
static SdfNodePtr onion(SdfNodePtr child, double thickness);
// ── Domain transform factories (one child) ──
/** @brief 创建周期重复节点 */
static SdfNodePtr repeat(SdfNodePtr child, const Point3D& cell);
/** @brief 创建 X 镜像节点 */
static SdfNodePtr mirror_x(SdfNodePtr child);
/** @brief 创建 Y 镜像节点 */
static SdfNodePtr mirror_y(SdfNodePtr child);
/** @brief 创建 Z 镜像节点 */
static SdfNodePtr mirror_z(SdfNodePtr child);
/** @brief 创建平移节点 */
static SdfNodePtr translate(SdfNodePtr child, const Point3D& offset);
/** @brief 创建旋转节点(绕 Y 轴) */
static SdfNodePtr rotate(SdfNodePtr child, double angle_rad);
/** @brief 创建缩放节点 */
static SdfNodePtr scale(SdfNodePtr child, const Point3D& factors);
/** @brief 创建扭曲节点 */
static SdfNodePtr twist(SdfNodePtr child, double amount);
/** @brief 创建弯曲节点 */
static SdfNodePtr bend(SdfNodePtr child, double amount);
/** @brief 创建拉伸节点 */
static SdfNodePtr elongate(SdfNodePtr child, const Point3D& h);
/** @brief 创建简化弯曲节点 */
static SdfNodePtr cheap_bend(SdfNodePtr child, double amount);
/** @brief 创建位移扰动节点 */
static SdfNodePtr displace(SdfNodePtr child, double amplitude, double frequency);
/**
* @brief 前序遍历所有节点
*
* 对树中每个节点调用回调函数 fn,遍历顺序为前序(父节点先于子节点)。
*
* @tparam Fn 可调用对象,签名为 void(SdfNode&)
*
* @code{.cpp}
* int leaf_count = 0;
* root->visit([&](const SdfNode& n) {
* if (n.children.empty()) ++leaf_count;
* });
* @endcode
*/
template <typename Fn>
void visit(Fn&& fn) {
fn(*this);
for (auto& c : children) c->visit(std::forward<Fn>(fn));
}
/**
* @brief 前序遍历(const 版本)
*
* @tparam Fn 可调用对象,签名为 void(const SdfNode&)
* @see visit(Fn&&)
*/
template <typename Fn>
void visit(Fn&& fn) const {
fn(*this);
for (auto& c : children) c->visit(std::forward<Fn>(fn));
}
public:
/**
* @brief 构造 SDF 节点
* @param o 操作类型
*/
explicit SdfNode(SdfOp o) : op(o) {}
};
/**
* @brief 在空间点 p 处求值 SDF 树
*
* 递归遍历表达式树,在叶节点调用对应图元的 SDF 函数,
* 在内部节点执行 CSG 操作或域变形。
*
* **算法概要:**
* 1. 域变形节点先对 p 做变换,将变换后的点传入子节点
* 2. CSG 布尔节点分别求值两个子节点后组合
* 3. 图元节点直接调用内联 SDF 函数
*
* @param root SDF 表达式树的根节点
* @param p 世界空间中的采样点
* @return 有符号距离(负 = 内部,零 = 表面,正 = 外部)
*
* @code{.cpp}
* auto root = SdfNode::sphere(1.0);
* double d = evaluate(root, Point3D(0.5, 0.0, 0.0)); // ≈ -0.5
* @endcode
*
* @see estimate_bounds 估计树的包围盒
*/
[[nodiscard]] double evaluate(const SdfNodePtr& root, const Point3D& p);
/**
* @brief 估算 SDF 树的包围半尺寸
*
* 通过对叶节点包围盒的递归合并,估算从树根到最远角落的半径。
* 结果是包围盒的半边长(从中心到面的距离)。
*
* @param root SDF 树的根节点
* @param margin 额外安全边距(默认 1.0)
* @return 包围半边长 (hx, hy, hz)
*
* @note 变形节点(旋转/弯曲/扭曲等)会使包围盒放大,以保守估计覆盖变形后区域。
*
* @see estimate_bbox 完整 AABB 结果
*/
[[nodiscard]] Point3D estimate_bounds(const SdfNodePtr& root, double margin = 1.0);
/**
* @brief SDF 树的包围盒(AABB
*
* min/max 分别为最小和最大角点坐标,包围盒以原点对称时 min = -max。
*/
struct SdfBBox {
Point3D min; ///< 最小角点
Point3D max; ///< 最大角点
};
/**
* @brief 估算 SDF 树的完整轴对齐包围盒
*
* 在 estimate_bounds 基础上计算对称 AABB。
*
* @param root SDF 树的根节点
* @param margin 额外安全边距
* @return AABB 包围盒
*
* @code{.cpp}
* auto bbox = estimate_bbox(root, 0.5);
* // bbox.min = (-hx-0.5, -hy-0.5, -hz-0.5)
* // bbox.max = ( hx+0.5, hy+0.5, hz+0.5)
* @endcode
*/
[[nodiscard]] SdfBBox estimate_bbox(const SdfNodePtr& root, double margin = 1.0);
} // namespace vde::sdf