#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 #include #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 将网格序列化为二进制字节流 * * 返回的缓冲区需要在消费后通过 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