Files

1045 lines
34 KiB
C
Raw Permalink Normal View History

#pragma once
/**
* @file vde_capi.h
* @brief ViewDesignEngine C API — 跨语言边界接口
*
* 提供稳定的 C ABI 接口,使 Python、Rust、C# 等语言可以通过 FFI 调用
* ViewDesignEngine 的核心功能,无需依赖 C++ ABI。
*
* ## 设计原则
*
* 1. **不透明句柄**: 所有内部对象(网格、曲线、求解器等)通过 `typedef` 前向声明
* 隐藏为不透明指针类型,外部代码只能通过 API 函数操作
* 2. **C 兼容类型**: 仅使用 C 原生类型(int, double, const char*)作为参数和返回值
* 3. **输出通过指针**: 复数返回值(如顶点坐标、包围盒)通过输出参数传递,避免结构体 ABI 问题
* 4. **显式生命周期管理**: 每个 create/load 函数都有对应的 free/destroy 函数
*
* ## 句柄类型
*
* | C 类型 | 内部类型 | 创建方式 | 释放方式 |
* |------------------|--------------------|-----------------------------|-------------------|
* | VdeHandle | VdeContext | vde_create() | vde_destroy() |
* | VdeMeshHandle | HalfedgeMesh | vde_load_mesh(), 工厂函数 | vde_mesh_free() |
* | VdeCurveHandle | NurbsCurve | vde_curve_create_bezier() | vde_curve_free() |
* | VdeSolverHandle | ConstraintSolver | vde_solver_create() | vde_solver_free() |
* | VdeBodyHandle | BrepModel | vde_body_create_*() | vde_body_free() |
*
* ## 布尔操作编码
*
* | 操作 | 整数值 |
* |------|--------|
* | 并集 | 0 |
* | 交集 | 1 |
* | 差集 | 2 |
*
* ## 线程安全性
*
* - 每个 VdeHandle 是独立的上下文,不同句柄之间线程安全
* - 同一句柄上的操作不是线程安全的(调用方负责同步)
*
* @ingroup capi
* @see vde.h C++ 等价接口
*/
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// ═══════════════════════════════════════════════════════════
// 不透明句柄类型(在 vde.h 中已有前向声明)
// ═══════════════════════════════════════════════════════════
/** @brief 引擎上下文句柄 — 管理错误状态和内部资源 */
#ifndef VDE_HANDLE_DEFINED
typedef struct VdeContext* VdeHandle;
#endif
/** @brief 三角网格句柄 — 顶点+面集合 */
#ifndef VDE_MESH_HANDLE_DEFINED
typedef struct VdeMesh* VdeMeshHandle;
#endif
/** @brief 参数曲线句柄 — NURBS 曲线 */
#ifndef VDE_CURVE_HANDLE_DEFINED
typedef struct VdeCurve* VdeCurveHandle;
#endif
/** @brief 约束求解器句柄 — 二维草图约束 */
#ifndef VDE_SOLVER_HANDLE_DEFINED
typedef struct VdeSolver* VdeSolverHandle;
#endif
/** @brief B-Rep 实体句柄 — 边界表示模型 */
#ifndef VDE_BODY_HANDLE_DEFINED
typedef struct VdeBody* VdeBodyHandle;
#endif
// ═══════════════════════════════════════════════════════════
// 引擎生命周期
// ═══════════════════════════════════════════════════════════
/**
* @brief 创建 ViewDesignEngine 上下文
*
* 初始化一个独立的引擎实例。几乎所有后续操作都需要传入此句柄。
*
* @return 新的引擎句柄,不需要时通过 vde_destroy() 释放
*
* @code{.py}
* // Python ctypes 示例
* ctx = lib.vde_create()
* @endcode
*
* @see vde_destroy 释放上下文
*/
VdeHandle vde_create(void);
/**
* @brief 销毁引擎上下文
*
* 释放上下文及其内部资源。销毁后不能再使用该句柄。
*
* @param ctx 引擎上下文句柄
*
* @see vde_create
*/
void vde_destroy(VdeHandle ctx);
/**
* @brief 获取引擎版本字符串
*
* @return 语义版本号字符串(如 "0.5.0"),静态生命周期,无需释放
*
* @code{.c}
* printf("VDE version: %s\n", vde_version());
* @endcode
*/
const char* vde_version(void);
/**
* @brief 获取最后一次操作的错误描述
*
* 当某操作失败(如加载不存在的文件)时,可通过此函数获取详情。
*
* @param ctx 引擎上下文句柄
* @return 错误描述字符串(上下文的内部缓冲区,静态生命周期)
*
* @note 每次新的失败操作会覆盖此描述。成功操作不会清除它。
*/
const char* vde_last_error(VdeHandle ctx);
// ═══════════════════════════════════════════════════════════
// 网格 I/O
// ═══════════════════════════════════════════════════════════
/**
* @brief 从文件加载三角网格
*
* 支持 OBJ (.obj), STL (.stl), PLY (.ply) 三种格式。
* 格式通过文件扩展名自动检测。
*
* @param ctx 引擎上下文句柄
* @param path 文件路径(UTF-8 编码)
* @return 网格句柄,失败时返回 NULL(通过 vde_last_error() 获取原因)
*
* @code{.c}
* VdeMeshHandle mesh = vde_load_mesh(ctx, "model.obj");
* if (!mesh) {
* fprintf(stderr, "Load failed: %s\n", vde_last_error(ctx));
* return;
* }
* @endcode
*
* @see vde_mesh_free 释放网格
* @see vde_save_mesh 保存网格
*/
VdeMeshHandle vde_load_mesh(VdeHandle ctx, const char* path);
/**
* @brief 将三角网格保存为文件
*
* 当前支持 OBJ 格式输出。
*
* @param ctx 引擎上下文句柄
* @param mesh 网格句柄
* @param path 输出文件路径
* @param format 输出格式(当前仅 "obj" 有效)
* @return 成功返回 1,失败返回 0
*
* @see vde_load_mesh 加载网格
*/
int vde_save_mesh(VdeHandle ctx, VdeMeshHandle mesh, const char* path, const char* format);
// ═══════════════════════════════════════════════════════════
// 网格查询
// ═══════════════════════════════════════════════════════════
/**
* @brief 获取网格的顶点数量
*
* @param mesh 网格句柄(必须有效且非 NULL)
* @return 顶点数
*
* @see vde_mesh_num_faces 面数
*/
size_t vde_mesh_num_vertices(VdeMeshHandle mesh);
/**
* @brief 获取网格的三角面数量
*
* @param mesh 网格句柄
* @return 面数
*
* @see vde_mesh_num_vertices 顶点数
*/
size_t vde_mesh_num_faces(VdeMeshHandle mesh);
/**
* @brief 获取指定索引的顶点坐标
*
* 顶点索引从 0 开始,到 num_vertices - 1。
*
* @param mesh 网格句柄
* @param idx 顶点索引 [0, num_vertices)
* @param out_x [out] 顶点 X 坐标
* @param out_y [out] 顶点 Y 坐标
* @param out_z [out] 顶点 Z 坐标
*
* @pre idx < vde_mesh_num_vertices(mesh)
*/
void vde_mesh_get_vertex(VdeMeshHandle mesh, size_t idx,
double* out_x, double* out_y, double* out_z);
/**
* @brief 获取网格的轴对齐包围盒
*
* @param mesh 网格句柄
* @param out_min_x [out] 最小 X 坐标
* @param out_min_y [out] 最小 Y 坐标
* @param out_min_z [out] 最小 Z 坐标
* @param out_max_x [out] 最大 X 坐标
* @param out_max_y [out] 最大 Y 坐标
* @param out_max_z [out] 最大 Z 坐标
*/
void vde_mesh_get_bounds(VdeMeshHandle mesh,
double* out_min_x, double* out_min_y, double* out_min_z,
double* out_max_x, double* out_max_y, double* out_max_z);
/**
* @brief 释放网格句柄及其资源
*
* 释放后将不能再使用该句柄。
*
* @param mesh 网格句柄
*/
void vde_mesh_free(VdeMeshHandle mesh);
// ═══════════════════════════════════════════════════════════
// 网格处理
// ═══════════════════════════════════════════════════════════
/**
* @brief 网格简化
*
* 通过 edge-collapse 或类似算法减少三角形数量,
* 同时尽可能保留几何特征。
*
* @param ctx 引擎上下文句柄
* @param mesh 输入网格句柄(不修改输入)
* @param target_ratio 目标简化比率 [0, 1]0 = 不变,1 = 最大简化)
* @return 简化后的新网格句柄
*
* @code{.c}
* VdeMeshHandle simple = vde_mesh_simplify(ctx, mesh, 0.5); // 减少 50%
* @endcode
*
* @see vde_mesh_smooth 网格平滑
*/
VdeMeshHandle vde_mesh_simplify(VdeHandle ctx, VdeMeshHandle mesh, double target_ratio);
/**
* @brief 网格平滑(Laplacian 或 Taubin 平滑)
*
* 通过迭代平滑顶点位置改善网格质量,减少噪声。
*
* @param ctx 引擎上下文句柄
* @param mesh 输入网格句柄
* @param iterations 平滑迭代次数
* @return 平滑后的新网格句柄
*
* @see vde_mesh_simplify 网格简化
*/
VdeMeshHandle vde_mesh_smooth(VdeHandle ctx, VdeMeshHandle mesh, int iterations);
/**
* @brief 网格布尔运算
*
* 对两个三角网格执行布尔操作。
*
* @param ctx 引擎上下文句柄
* @param a 网格 A
* @param b 网格 B
* @param operation 布尔操作类型: 0 = 并集, 1 = 交集, 2 = 差集 (A \ B)
* @return 布尔结果的新网格句柄
*
* @warning 两个输入网格必须是水密的(closed manifold),否则结果不可预测。
*
* @code{.c}
* VdeMeshHandle result = vde_mesh_boolean(ctx, box, sphere, 0); // 并集
* @endcode
*/
VdeMeshHandle vde_mesh_boolean(VdeHandle ctx, VdeMeshHandle a, VdeMeshHandle b, int operation);
// ═══════════════════════════════════════════════════════════
// 曲线
// ═══════════════════════════════════════════════════════════
/**
* @brief 创建 Bézier 曲线
*
* @param ctx 引擎上下文句柄
* @param control_points 控制点数组,每 3 个 double 表示一个三维点 (x,y,z)
* @param num_points 控制点数量(次数 = num_points - 1
* @param dim 维度(固定为 3
* @return 曲线句柄
*
* @code{.c}
* double cp[] = {0,0,0, 1,2,0, 3,0,0}; // 3 控制点,2 次 Bézier
* VdeCurveHandle curve = vde_curve_create_bezier(ctx, cp, 3, 3);
* @endcode
*
* @see vde_curve_evaluate 求值
* @see vde_curve_free 释放
*/
VdeCurveHandle vde_curve_create_bezier(VdeHandle ctx, const double* control_points,
int num_points, int dim);
/**
* @brief 在参数 t 处求值曲线
*
* @param curve 曲线句柄
* @param t 参数值 [0, 1]
* @param out_x [out] 曲线上点的 X 坐标
* @param out_y [out] 曲线上点的 Y 坐标
* @param out_z [out] 曲线上点的 Z 坐标
*
* @pre 0 ≤ t ≤ 1
*/
void vde_curve_evaluate(VdeCurveHandle curve, double t,
double* out_x, double* out_y, double* out_z);
/**
* @brief 获取曲线的次数
*
* 对于 Bézier 曲线,次数 = 控制点数 - 1。
*
* @param curve 曲线句柄
* @return 曲线次数
*/
int vde_curve_degree(VdeCurveHandle curve);
/**
* @brief 释放曲线句柄
*
* @param curve 曲线句柄
*/
void vde_curve_free(VdeCurveHandle curve);
// ═══════════════════════════════════════════════════════════
// 碰撞检测
// ═══════════════════════════════════════════════════════════
/**
* @brief 两球体碰撞检测(GJK 算法)
*
* 用于验证和测试 GJK 碰撞检测算法的轻量级接口。
*
* @param ctx 引擎上下文句柄
* @param cx1, cy1, cz1 第一个球体的球心坐标
* @param r1 第一个球体的半径
* @param cx2, cy2, cz2 第二个球体的球心坐标
* @param r2 第二个球体的半径
* @return 碰撞返回 1,不碰撞返回 0
*
* @code{.c}
* int hit = vde_collision_gjk_spheres(ctx, 0,0,0, 1.0, 1.5,0,0, 0.5);
* @endcode
*
* @see vde_collision_ray_mesh 射线-网格相交
*/
int vde_collision_gjk_spheres(VdeHandle ctx,
double cx1, double cy1, double cz1, double r1,
double cx2, double cy2, double cz2, double r2);
/**
* @brief 射线与网格的最近交点查询
*
* 使用 BVH 加速结构加速查询。
*
* @param ctx 引擎上下文句柄
* @param mesh 目标网格句柄
* @param ox, oy, oz 射线起点
* @param dx, dy, dz 射线方向(无需归一化)
* @param out_t [out] 交点参数 t(沿射线的距离)
* @param out_x, out_y, out_z [out] 交点坐标
* @return 命中返回 1,未命中返回 0
*
* @code{.c}
* double t, hx, hy, hz;
* int hit = vde_collision_ray_mesh(ctx, mesh,
* 0,0,-10, // 射线起点
* 0,0,1, // 射线方向 (沿 Z 轴正方向)
* &t, &hx, &hy, &hz);
* if (hit) printf("Hit at (%f,%f,%f), t=%f\n", hx, hy, hz, t);
* @endcode
*
* @see vde_collision_gjk_spheres GJK 球体碰撞
*/
int vde_collision_ray_mesh(VdeHandle ctx, VdeMeshHandle mesh,
double ox, double oy, double oz,
double dx, double dy, double dz,
double* out_t, double* out_x, double* out_y, double* out_z);
// ═══════════════════════════════════════════════════════════
// 约束求解器(二维草图)
// ═══════════════════════════════════════════════════════════
/**
* @brief 创建二维约束求解器
*
* 用于求解草图几何约束(距离、水平、垂直等)。
*
* @return 求解器句柄
*
* @see vde_solver_free 释放
*/
VdeSolverHandle vde_solver_create(void);
/**
* @brief 向求解器添加点
*
* @param solver 求解器句柄
* @param x 初始 X 坐标
* @param y 初始 Y 坐标
* @param fixed 固定点标记(1 = 固定不移动)
* @return 点的 ID(用于后续约束引用)
*
* @see vde_solver_add_line 添加线段
*/
int vde_solver_add_point(VdeSolverHandle solver, double x, double y, int fixed);
/**
* @brief 向求解器添加线段(由两个点定义)
*
* @param solver 求解器句柄
* @param p0 起点 ID
* @param p1 终点 ID
* @return 线段的 ID
*
* @see vde_solver_add_point 添加点
* @see vde_solver_add_horizontal 添加水平约束
* @see vde_solver_add_vertical 添加垂直约束
*/
int vde_solver_add_line(VdeSolverHandle solver, int p0, int p1);
/**
* @brief 添加距离约束(两点之间)
*
* @param solver 求解器句柄
* @param p0 第一个点的 ID
* @param p1 第二个点的 ID
* @param d 目标距离(必须 > 0
*
* @see vde_solver_add_horizontal 水平约束
*/
void vde_solver_add_distance_constraint(VdeSolverHandle solver, int p0, int p1, double d);
/**
* @brief 添加水平约束(线段水平)
*
* @param solver 求解器句柄
* @param line_id 线段 ID
*
* @see vde_solver_add_vertical 垂直约束
* @see vde_solver_add_line 添加线段
*/
void vde_solver_add_horizontal(VdeSolverHandle solver, int line_id);
/**
* @brief 添加垂直约束(线段垂直)
*
* @param solver 求解器句柄
* @param line_id 线段 ID
*
* @see vde_solver_add_horizontal 水平约束
*/
void vde_solver_add_vertical(VdeSolverHandle solver, int line_id);
/**
* @brief 求解约束系统
*
* 通过迭代优化使所有点的位置满足所有约束。
*
* @param solver 求解器句柄
* @param max_iter 最大迭代次数
* @param tol 收敛容差
* @return 收敛返回 1,未收敛返回 0
*
* @code{.c}
* int converged = vde_solver_solve(solver, 100, 1e-6);
* @endcode
*
* @see vde_solver_get_point 获取求解后的点坐标
*/
int vde_solver_solve(VdeSolverHandle solver, int max_iter, double tol);
/**
* @brief 获取求解后的点坐标
*
* @param solver 求解器句柄
* @param idx 点 ID
* @param out_x [out] 求解后 X 坐标
* @param out_y [out] 求解后 Y 坐标
*
* @pre 必须先调用 vde_solver_solve()
*/
void vde_solver_get_point(VdeSolverHandle solver, int idx, double* out_x, double* out_y);
/**
* @brief 释放求解器句柄
*
* @param solver 求解器句柄
*/
void vde_solver_free(VdeSolverHandle solver);
// ═══════════════════════════════════════════════════════════
// B-Rep 建模
// ═══════════════════════════════════════════════════════════
/**
* @brief 创建实心立方体 B-Rep
*
* 以原点为中心的轴对齐立方体。
*
* @param ctx 引擎上下文句柄
* @param w 宽度(X 方向),> 0
* @param h 高度(Y 方向),> 0
* @param d 深度(Z 方向),> 0
* @return B-Rep 实体句柄
*
* @see vde_body_create_cylinder 圆柱体
* @see vde_body_create_sphere 球体
*/
VdeBodyHandle vde_body_create_box(VdeHandle ctx, double w, double h, double d);
/**
* @brief 创建实心圆柱体 B-Rep
*
* 沿 Y 轴、以原点为中心的圆柱体。
*
* @param ctx 引擎上下文句柄
* @param r 截面半径,> 0
* @param h 总高度(从 -h/2 到 +h/2
* @param segs 截面分段数(边数)
* @return B-Rep 实体句柄
*
* @see vde_body_create_box 立方体
*/
VdeBodyHandle vde_body_create_cylinder(VdeHandle ctx, double r, double h, int segs);
/**
* @brief 创建球体 B-Rep
*
* @param ctx 引擎上下文句柄
* @param r 球半径,> 0
* @param su 经度方向分段数
* @param sv 纬度方向分段数
* @return B-Rep 实体句柄
*/
VdeBodyHandle vde_body_create_sphere(VdeHandle ctx, double r, int su, int sv);
/**
* @brief 将 B-Rep 实体转换为三角网格
*
* 以指定的弦高误差对曲面进行 tessellation。
*
* @param ctx 引擎上下文句柄
* @param body B-Rep 实体句柄
* @param deflection 弦高误差(越小 = 越精细,推荐 0.01)
* @return 三角网格句柄
*
* @code{.c}
* VdeBodyHandle body = vde_body_create_box(ctx, 10, 5, 3);
* VdeMeshHandle mesh = vde_body_to_mesh(ctx, body, 0.01);
* vde_save_mesh(ctx, mesh, "box.obj", "obj");
* @endcode
*
* @see vde_body_free 释放 B-Rep 实体
*/
VdeMeshHandle vde_body_to_mesh(VdeHandle ctx, VdeBodyHandle body, double deflection);
/**
* @brief 释放 B-Rep 实体句柄
*
* @param body B-Rep 实体句柄
*/
void vde_body_free(VdeBodyHandle body);
/**
* @brief 深拷贝 B-Rep 实体
*
* 创建 B-Rep 实体的完整独立副本,修改副本不影响原实体。
*
* @param ctx 引擎上下文句柄
* @param body 源 B-Rep 实体句柄(不会被修改)
* @return 新的 B-Rep 实体句柄,调用方通过 vde_body_free() 释放
*
* @note 深拷贝会复制所有几何数据(曲面、边、顶点),开销随复杂度增长。
*
* @see vde_body_free
* @ingroup capi
*/
VdeBodyHandle vde_body_clone(VdeHandle ctx, VdeBodyHandle body);
// ── B-Rep STEP/IGES I/O ──
/**
* @brief 从 STEP 文件导入 B-Rep 实体列表
*
* STEPISO 10303)是工业标准的 CAD 数据交换格式。
* 一个 STEP 文件可能包含多个实体,所有实体同时加载。
*
* @param ctx 引擎上下文句柄
* @param path STEP 文件路径(UTF-8 编码)
* @param out_bodies [out] 指向实体句柄数组的指针,由库分配,调用方通过 vde_body_free_array() 释放
* @return 导入的实体数量,失败返回 0(通过 vde_last_error() 获取原因)
*
* @warning 若返回 0out_bodies 的值未定义,不应使用或释放
*
* @note 内存管理:成功时库分配 VdeBodyHandle 数组,调用方必须调用
* vde_body_free_array(out_bodies, count) 释放。
* 数组中每个 body 由 free_array 统一释放,调用方不应单独 vde_body_free。
*
* @code{.c}
* VdeBodyHandle* bodies = NULL;
* int count = vde_body_load_step(ctx, \"assembly.stp\", &bodies);
* for (int i = 0; i < count; i++) {
* VdeMeshHandle mesh = vde_body_to_mesh(ctx, bodies[i], 0.01);
* // ... use mesh ...
* vde_mesh_free(mesh);
* }
* vde_body_free_array(bodies, count);
* @endcode
*
* @see vde_body_save_step, vde_body_load_iges, vde_body_free_array
* @ingroup capi
*/
int vde_body_load_step(VdeHandle ctx, const char* path, VdeBodyHandle** out_bodies);
/**
* @brief 从 IGES 文件导入 B-Rep 实体列表
*
* IGESInitial Graphics Exchange Specification)是较早期的 CAD 交换格式。
*
* @param ctx 引擎上下文句柄
* @param path IGES 文件路径(UTF-8 编码)
* @param out_bodies [out] 指向实体句柄数组的指针,由库分配,调用方通过 vde_body_free_array() 释放
* @return 导入的实体数量,失败返回 0
*
* @note 内存管理约定与 vde_body_load_step 相同:成功时库分配数组,调用方负责 vde_body_free_array。
*
* @see vde_body_save_iges, vde_body_load_step
* @ingroup capi
*/
int vde_body_load_iges(VdeHandle ctx, const char* path, VdeBodyHandle** out_bodies);
/**
* @brief 将 B-Rep 实体列表导出为 STEP 文件
*
* @param ctx 引擎上下文句柄
* @param bodies B-Rep 实体句柄数组(调用方管理生命周期)
* @param count 实体数量
* @param path 输出文件路径
* @return 成功返回 1,失败返回 0
*
* @note bodies 数组的所有权不转移给此函数,调用方继续管理。
*
* @see vde_body_load_step
* @ingroup capi
*/
int vde_body_save_step(VdeHandle ctx, VdeBodyHandle* bodies, int count, const char* path);
/**
* @brief 将 B-Rep 实体列表导出为 IGES 文件
*
* @param ctx 引擎上下文句柄
* @param bodies B-Rep 实体句柄数组(调用方管理生命周期)
* @param count 实体数量
* @param path 输出文件路径
* @return 成功返回 1,失败返回 0
*
* @see vde_body_load_iges
* @ingroup capi
*/
int vde_body_save_iges(VdeHandle ctx, VdeBodyHandle* bodies, int count, const char* path);
// ── B-Rep Boolean ──
/**
* @brief B-Rep 实体布尔运算
*
* 对两个边界表示实体执行精确布尔运算(基于 Parasolid 风格的边界求交)。
* 与网格布尔运算相比,B-Rep 布尔运算结果更精确,适合工程应用。
*
* @param ctx 引擎上下文句柄
* @param a 第一个 B-Rep 实体句柄
* @param b 第二个 B-Rep 实体句柄
* @param op 布尔操作类型: 0 = 并集(Union),1 = 交集(Intersection),2 = 差集(DifferenceA \\ B
* @return 布尔结果的新 B-Rep 实体句柄,调用方通过 vde_body_free() 释放
*
* @warning 输入实体必须是水密的(closed manifold),否则结果未定义
*
* @code{.c}
* VdeBodyHandle box = vde_body_create_box(ctx, 2, 2, 2);
* VdeBodyHandle sphere = vde_body_create_sphere(ctx, 1.5, 32, 32);
* VdeBodyHandle result = vde_body_boolean(ctx, box, sphere, 0); // 并集
* vde_body_free(box);
* vde_body_free(sphere);
* // ... use result ...
* vde_body_free(result);
* @endcode
*
* @see vde_mesh_boolean 网格级别布尔运算
* @ingroup capi
*/
VdeBodyHandle vde_body_boolean(VdeHandle ctx, VdeBodyHandle a, VdeBodyHandle b, int op);
// ── Mesh face data access ──
/**
* @brief 获取网格面的顶点索引
*
* 返回指定三角面的顶点索引列表。对于三角网格,通常返回 3 个索引。
*
* @param mesh 网格句柄
* @param face_idx 面索引 [0, vde_mesh_num_faces(mesh))
* @param out_indices [out] 指向顶点索引数组的指针,由库分配,调用方必须通过 vde_free_buffer() 释放
* @return 顶点数量(三角网格通常为 3),失败返回 -1
*
* @note 内存管理:成功时库在内部分配 int 数组,out_indices 指向该数组。
* 调用方使用完毕后必须调用 vde_free_buffer(out_indices) 释放。
*
* @code{.c}
* int* indices = NULL;
* int n = vde_mesh_get_face(mesh, 0, &indices);
* if (n > 0) {
* printf(\"Face 0: v%d, v%d, v%d\\n\", indices[0], indices[1], indices[2]);
* vde_free_buffer(indices);
* }
* @endcode
*
* @see vde_mesh_get_vertex, vde_free_buffer
* @ingroup capi
*/
int vde_mesh_get_face(VdeMeshHandle mesh, size_t face_idx, int** out_indices);
// ═══════════════════════════════════════════════════════════
// SDF 建模
// ═══════════════════════════════════════════════════════════
/** @brief SDF 节点句柄(不透明) */
#ifndef VDE_SDF_HANDLE_DEFINED
typedef struct VdeSdfNode* VdeSdfHandle;
#endif
/**
* @brief 创建 SDF 球体节点
*
* 以原点为中心、指定半径的球体隐式曲面。
*
* @param ctx 引擎上下文句柄
* @param radius 球体半径,> 0
* @return SDF 节点句柄,调用方通过 vde_sdf_free() 释放
*
* @note 球体公式:f(x,y,z) = |(x,y,z)| - radius
*
* @see vde_sdf_box, vde_sdf_cylinder, vde_sdf_to_mesh
* @ingroup capi
*/
VdeSdfHandle vde_sdf_sphere(VdeHandle ctx, double radius);
/**
* @brief 创建 SDF 盒子节点
*
* 以原点为中心的轴对齐立方体隐式曲面。
*
* @param ctx 引擎上下文句柄
* @param hx X 轴半边长,> 0
* @param hy Y 轴半边长,> 0
* @param hz Z 轴半边长,> 0
* @return SDF 节点句柄
*
* @note 盒子范围为 [-hx, hx] × [-hy, hy] × [-hz, hz]
*
* @see vde_sdf_sphere
* @ingroup capi
*/
VdeSdfHandle vde_sdf_box(VdeHandle ctx, double hx, double hy, double hz);
/**
* @brief 创建 SDF 圆柱节点
*
* 沿 Y 轴、以原点为中心的圆柱体隐式曲面。
*
* @param ctx 引擎上下文句柄
* @param radius 截面半径,> 0
* @param height 总高度(从 -height/2 到 +height/2
* @return SDF 节点句柄
*
* @see vde_sdf_sphere, vde_sdf_box
* @ingroup capi
*/
VdeSdfHandle vde_sdf_cylinder(VdeHandle ctx, double radius, double height);
/**
* @brief SDF 布尔并集
*
* 返回两个 SDF 节点的并集,新节点拥有 a 和 b。
* 并集操作使两个形状合并。
*
* @param ctx 引擎上下文句柄
* @param a 第一个 SDF 节点(所有权转移给结果,调用方不应单独释放)
* @param b 第二个 SDF 节点(所有权转移给结果,调用方不应单独释放)
* @return 并集 SDF 节点句柄
*
* @warning 传入 a 和 b 后,调用方不得再单独释放它们;释放结果节点时会一并释放。
*
* @see vde_sdf_intersection, vde_sdf_difference
* @ingroup capi
*/
VdeSdfHandle vde_sdf_union(VdeHandle ctx, VdeSdfHandle a, VdeSdfHandle b);
/**
* @brief SDF 布尔交集
*
* 返回两个 SDF 节点的交集,得到共同区域。
*
* @param ctx 引擎上下文句柄
* @param a 第一个 SDF 节点(所有权转移给结果)
* @param b 第二个 SDF 节点(所有权转移给结果)
* @return 交集 SDF 节点句柄
*
* @note 所有权语义同 vde_sdf_union
*
* @see vde_sdf_union, vde_sdf_difference
* @ingroup capi
*/
VdeSdfHandle vde_sdf_intersection(VdeHandle ctx, VdeSdfHandle a, VdeSdfHandle b);
/**
* @brief SDF 布尔差集
*
* 返回 A 减去 B 的差集。
*
* @param ctx 引擎上下文句柄
* @param a 被减 SDF 节点(所有权转移给结果)
* @param b 减去 SDF 节点(所有权转移给结果)
* @return 差集 SDF 节点句柄(A \\ B
*
* @note 所有权语义同 vde_sdf_union
*
* @see vde_sdf_union, vde_sdf_intersection
* @ingroup capi
*/
VdeSdfHandle vde_sdf_difference(VdeHandle ctx, VdeSdfHandle a, VdeSdfHandle b);
/**
* @brief 将 SDF 转换为三角网格
*
* 通过 Marching Cubes 算法在给定分辨率下将隐式曲面离散化为三角网格。
*
* @param ctx 引擎上下文句柄
* @param sdf SDF 节点句柄
* @param resolution 体素网格分辨率(如 128),越高越精细,内存开销为 O(res³)
* @return 三角网格句柄,调用方通过 vde_mesh_free() 释放
*
* @warning resolution 过大会导致极高内存消耗(128³ = 2M 体素,256³ = 16M 体素)
*
* @code{.c}
* VdeSdfHandle s = vde_sdf_sphere(ctx, 1.0);
* VdeMeshHandle mesh = vde_sdf_to_mesh(ctx, s, 64); // 64³ 体素
* vde_save_mesh(ctx, mesh, \"sphere.obj\", \"obj\");
* vde_mesh_free(mesh);
* vde_sdf_free(s);
* @endcode
*
* @see vde_sdf_sphere, vde_sdf_box
* @ingroup capi
*/
VdeMeshHandle vde_sdf_to_mesh(VdeHandle ctx, VdeSdfHandle sdf, int resolution);
/**
* @brief 释放 SDF 节点
*
* 释放一个 SDF 节点及其所有子节点。
* 如果是通过布尔操作创建的复合节点,其所有子节点同时释放。
*
* @param sdf SDF 节点句柄
*
* @note 将 sdf 传给布尔操作(union/intersection/difference)后,
* 所有权已转移,不得再调用 vde_sdf_free 释放。
*
* @see vde_sdf_union
* @ingroup capi
*/
void vde_sdf_free(VdeSdfHandle sdf);
// ═══════════════════════════════════════════════════════════
// 装配体
// ═══════════════════════════════════════════════════════════
/** @brief 装配体句柄 */
#ifndef VDE_ASSEMBLY_HANDLE_DEFINED
typedef struct VdeAssembly* VdeAssemblyHandle;
#endif
/**
* @brief 创建装配体
*
* 创建一个空的装配体容器,用于组织多个零件。
*
* @param ctx 引擎上下文句柄
* @param name 装配体名称(内部复制,调用方可随后释放)
* @return 装配体句柄,调用方通过 vde_assembly_free() 释放
*
* @note 装配体本身不拥有其包含的 body,释放装配体不会释放其中的 body。
*
* @see vde_assembly_add_part, vde_assembly_free
* @ingroup capi
*/
VdeAssemblyHandle vde_assembly_create(VdeHandle ctx, const char* name);
/**
* @brief 向装配体根节点添加零件
*
* 将一个 B-Rep 实体以指定名称添加到装配体的根层级。
*
* @param assembly 装配体句柄
* @param name 零件名称(内部复制)
* @param body B-Rep 实体句柄(所有权不转移,调用方继续管理)
* @return 成功返回 1,失败返回 0
*
* @note body 的生命周期由调用方管理;vde_assembly_free 不会释放 body。
*
* @warning 添加后调用方仍拥有 body,必须先 vde_assembly_free 再 vde_body_free
*
* @see vde_assembly_create, vde_assembly_part_count
* @ingroup capi
*/
int vde_assembly_add_part(VdeAssemblyHandle assembly, const char* name, VdeBodyHandle body);
/**
* @brief 获取装配体零件数量
*
* @param assembly 装配体句柄
* @return 零件总数
*
* @see vde_assembly_add_part
* @ingroup capi
*/
int vde_assembly_part_count(VdeAssemblyHandle assembly);
/**
* @brief 释放装配体
*
* 释放装配体结构,但不释放其中包含的 body 句柄。
* 调用方需在释放装配体后自行释放各个 body。
*
* @param assembly 装配体句柄
*
* @note 不释放 body:装配体只是引用 body,不拥有其所有权。
* 释放顺序:先 vde_assembly_free(assembly),再 vde_body_free(body)。
*
* @see vde_assembly_create
* @ingroup capi
*/
void vde_assembly_free(VdeAssemblyHandle assembly);
/**
* @brief 释放 body 句柄数组
*
* 释放 vde_body_load_step / vde_body_load_iges 返回的 VdeBodyHandle 数组
* 及其中的所有 B-Rep 实体。
*
* @param bodies body 句柄数组(由 load_step/load_iges 返回)
* @param count 数组中的实体数量
*
* @note 此函数同时释放数组中所有 body 和数组本身。
* 调用方不需要(也不应该)对数组中每个元素单独调用 vde_body_free。
*
* @code{.c}
* VdeBodyHandle* bodies = NULL;
* int count = vde_body_load_step(ctx, \"file.stp\", &bodies);
* // ... use bodies[0..count-1] ...
* vde_body_free_array(bodies, count); // 一次性释放所有
* @endcode
*
* @see vde_body_load_step, vde_body_load_iges
* @ingroup capi
*/
void vde_body_free_array(VdeBodyHandle* bodies, int count);
// ═══════════════════════════════════════════════════════════
// 序列化
// ═══════════════════════════════════════════════════════════
/**
* @brief 将网格序列化为二进制字节流
*
* 返回的缓冲区需要在消费后通过 vde_free_buffer() 释放。
*
* @param ctx 引擎上下文句柄
* @param mesh 网格句柄
* @param out_data [out] 指向序列化字节数组的指针
* @return 字节数
*
* @note 输出的缓冲区由库管理,调用方不得直接 free/delete。
*
* @code{.c}
* uint8_t* data = NULL;
* size_t len = vde_serialize_mesh(ctx, mesh, &data);
* // ... 使用/存储 data ...
* vde_free_buffer(data);
* @endcode
*
* @see vde_deserialize_mesh 反序列化
* @see vde_free_buffer 释放序列化缓冲区
*/
size_t vde_serialize_mesh(VdeHandle ctx, VdeMeshHandle mesh, uint8_t** out_data);
/**
* @brief 从二进制字节流反序列化为网格
*
* @param ctx 引擎上下文句柄
* @param data 序列化数据缓冲区
* @param len 数据长度(字节数)
* @return 反序列化后的网格句柄
*
* @see vde_serialize_mesh 序列化
*/
VdeMeshHandle vde_deserialize_mesh(VdeHandle ctx, const uint8_t* data, size_t len);
/**
* @brief 释放由 vde_serialize_mesh 分配的缓冲区
*
* @param ptr 由 vde_serialize_mesh 返回的缓冲区指针
*/
void vde_free_buffer(uint8_t* ptr);
#ifdef __cplusplus
}
#endif