diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index f60fd9b..999346c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -9,3 +9,4 @@ add_subdirectory(08_3d_print) add_subdirectory(09_brep_fab) add_subdirectory(10_flange) add_subdirectory(11_gear) +add_subdirectory(capi) diff --git a/examples/capi/CMakeLists.txt b/examples/capi/CMakeLists.txt new file mode 100644 index 0000000..34187e7 --- /dev/null +++ b/examples/capi/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(basic_usage basic_usage.cpp) +target_link_libraries(basic_usage PRIVATE vde) diff --git a/examples/capi/basic_usage.cpp b/examples/capi/basic_usage.cpp new file mode 100644 index 0000000..ec691c6 --- /dev/null +++ b/examples/capi/basic_usage.cpp @@ -0,0 +1,350 @@ +/** + * @file basic_usage.cpp + * @brief ViewDesignEngine C API 基础使用示例 + * + * 演示通过纯 C 接口使用 VDE 引擎的主要功能: + * - 引擎生命周期管理 + * - Mesh 加载体素 → 查询 → 保存 + * - 网格简化与平滑 + * - 碰撞检测(GJK + 射线-网格) + * - Bézier 曲线创建与求值 + * - B-Rep 实体创建 → 布尔运算 → 转网格 + * - SDF 隐式曲面 → 转网格 + * - 序列化与反序列化 + * + * 编译: g++ -std=c++17 -I../include basic_usage.cpp -L../build -lvde -o basic_usage + */ + +#include +#include +#include + +/* C API 头文件(在 C++ 中以 extern "C" 方式暴露) */ +#include "vde/capi/vde_capi.h" + +/* ── 辅助宏 ─────────────────────────────────────────────── */ +#define CHECK(cond, msg) do { \ + if (!(cond)) { \ + fprintf(stderr, "[FAIL] %s\n", msg); \ + return 1; \ + } \ +} while(0) + +/* ── 示例 1: 引擎生命周期 ──────────────────────────────── */ +static void demo01_lifecycle(void) { + printf("=== 示例 1: 引擎生命周期 ===\n"); + + VdeHandle ctx = vde_create(); + printf(" 引擎创建成功\n"); + + const char* ver = vde_version(); + printf(" VDE 版本: %s\n", ver); + + vde_destroy(ctx); + printf(" 引擎销毁成功\n\n"); +} + +/* ── 示例 2: Mesh 创建/查询/保存 ───────────────────────── */ +static int demo02_mesh_io(void) { + printf("=== 示例 2: Mesh I/O ===\n"); + + VdeHandle ctx = vde_create(); + + /* 2.1 创建 B-Rep box → 转 mesh(模拟加载) */ + VdeBodyHandle body = vde_body_create_box(ctx, 2.0, 2.0, 2.0); + CHECK(body != NULL, "vde_body_create_box 失败"); + + VdeMeshHandle mesh = vde_body_to_mesh(ctx, body, 0.01); + CHECK(mesh != NULL, "vde_body_to_mesh 失败"); + + /* 2.2 查询网格属性 */ + size_t nv = vde_mesh_num_vertices(mesh); + size_t nf = vde_mesh_num_faces(mesh); + printf(" 顶点数: %zu, 三角面数: %zu\n", nv, nf); + + /* 2.3 获取包围盒 */ + double min_x, min_y, min_z, max_x, max_y, max_z; + vde_mesh_get_bounds(mesh, &min_x, &min_y, &min_z, &max_x, &max_y, &max_z); + printf(" 包围盒: (%.1f, %.1f, %.1f) - (%.1f, %.1f, %.1f)\n", + min_x, min_y, min_z, max_x, max_y, max_z); + + /* 2.4 获取第一个顶点 */ + if (nv > 0) { + double vx, vy, vz; + vde_mesh_get_vertex(mesh, 0, &vx, &vy, &vz); + printf(" 顶点 [0]: (%.3f, %.3f, %.3f)\n", vx, vy, vz); + } + + /* 2.5 获取第一个面的索引 — 演示 vde_mesh_get_face + vde_free_buffer */ + if (nf > 0) { + int* indices = NULL; + int n = vde_mesh_get_face(mesh, 0, &indices); + if (n > 0) { + printf(" 面 [0]: v%d v%d v%d\n", indices[0], indices[1], indices[2]); + } + vde_free_buffer((uint8_t*)indices); /* 释放库分配的索引数组 */ + } + + /* 2.6 保存为 OBJ */ + int saved = vde_save_mesh(ctx, mesh, "/tmp/vde_capi_demo_box.obj", "obj"); + printf(" 保存 OBJ: %s\n", saved ? "成功" : "失败"); + + vde_mesh_free(mesh); + vde_body_free(body); + vde_destroy(ctx); + printf(" Mesh I/O 演示完成\n\n"); + return 0; +} + +/* ── 示例 3: 网格简化与平滑 ────────────────────────────── */ +static int demo03_mesh_process(void) { + printf("=== 示例 3: 网格处理 ===\n"); + + VdeHandle ctx = vde_create(); + + /* 创建球体 → 网格(较多三角形,便于观察简化效果) */ + VdeBodyHandle sphere = vde_body_create_sphere(ctx, 2.0, 32, 32); + VdeMeshHandle mesh = vde_body_to_mesh(ctx, sphere, 0.01); + printf(" 原始球体: %zu 顶点, %zu 面\n", + vde_mesh_num_vertices(mesh), vde_mesh_num_faces(mesh)); + + /* 网格简化到 50% */ + VdeMeshHandle simplified = vde_mesh_simplify(ctx, mesh, 0.5); + if (simplified) { + printf(" 简化后: %zu 顶点, %zu 面 (目标 50%%)\n", + vde_mesh_num_vertices(simplified), + vde_mesh_num_faces(simplified)); + vde_mesh_free(simplified); + } + + /* 网格平滑 */ + VdeMeshHandle smoothed = vde_mesh_smooth(ctx, mesh, 3); + if (smoothed) { + printf(" 平滑后: %zu 顶点, %zu 面 (3 次迭代)\n", + vde_mesh_num_vertices(smoothed), + vde_mesh_num_faces(smoothed)); + vde_mesh_free(smoothed); + } + + vde_mesh_free(mesh); + vde_body_free(sphere); + vde_destroy(ctx); + printf(" 网格处理演示完成\n\n"); + return 0; +} + +/* ── 示例 4: 碰撞检测 ──────────────────────────────────── */ +static int demo04_collision(void) { + printf("=== 示例 4: 碰撞检测 ===\n"); + + VdeHandle ctx = vde_create(); + + /* 4.1 GJK 球体碰撞 — 两球相交 */ + int hit = vde_collision_gjk_spheres(ctx, + 0.0, 0.0, 0.0, 1.0, /* 球 1: 原点, 半径 1 */ + 1.5, 0.0, 0.0, 1.0); /* 球 2: (1.5,0,0), 半径 1 → 圆心距 1.5 < 1+1=2 */ + printf(" GJK 两球相交: %s (期望: 1)\n", hit ? "是" : "否"); + + /* 相距远的球 — 不应碰撞 */ + hit = vde_collision_gjk_spheres(ctx, + 0.0, 0.0, 0.0, 1.0, + 5.0, 0.0, 0.0, 1.0); /* 圆心距 5 > 2 */ + printf(" GJK 两球分离: %s (期望: 0)\n", hit ? "是" : "否"); + + /* 4.2 射线-网格碰撞 — 从 Z+ 方向射向一个立方体 */ + VdeBodyHandle box = vde_body_create_box(ctx, 2.0, 2.0, 2.0); + VdeMeshHandle mesh = vde_body_to_mesh(ctx, box, 0.01); + double t, hx, hy, hz; + int ray_hit = vde_collision_ray_mesh(ctx, mesh, + 0.0, 0.0, 5.0, /* 射线起点: (0,0,5) */ + 0.0, 0.0, -1.0, /* 方向: 沿 Z 负方向 */ + &t, &hx, &hy, &hz); + if (ray_hit) { + printf(" 射线命中: t=%.3f, 交点=(%.3f, %.3f, %.3f)\n", + t, hx, hy, hz); + } else { + printf(" 射线未命中 (不应该)\n"); + } + + vde_mesh_free(mesh); + vde_body_free(box); + vde_destroy(ctx); + printf(" 碰撞检测演示完成\n\n"); + return 0; +} + +/* ── 示例 5: Bézier 曲线 ───────────────────────────────── */ +static int demo05_curve(void) { + printf("=== 示例 5: Bézier 曲线 ===\n"); + + VdeHandle ctx = vde_create(); + + /* 创建 3 阶 (4 控制点) Bézier 曲线 */ + double cp[] = { + 0.0, 0.0, 0.0, /* P0 */ + 1.0, 2.0, 0.0, /* P1 */ + 2.0, 2.0, 0.0, /* P2 */ + 3.0, 0.0, 0.0 /* P3 */ + }; + VdeCurveHandle curve = vde_curve_create_bezier(ctx, cp, 4, 3); + CHECK(curve != NULL, "vde_curve_create_bezier 失败"); + + printf(" 曲线次数: %d\n", vde_curve_degree(curve)); + + /* 在参数 t = 0.0, 0.5, 1.0 处求值 */ + for (int i = 0; i <= 2; i++) { + double t = i * 0.5; + double px, py, pz; + vde_curve_evaluate(curve, t, &px, &py, &pz); + printf(" t=%.1f → (%.3f, %.3f, %.3f)\n", t, px, py, pz); + } + + vde_curve_free(curve); + vde_destroy(ctx); + printf(" 曲线演示完成\n\n"); + return 0; +} + +/* ── 示例 6: B-Rep 实体与布尔运算 ─────────────────────── */ +static int demo06_brep_boolean(void) { + printf("=== 示例 6: B-Rep 布尔运算 ===\n"); + + VdeHandle ctx = vde_create(); + + /* 创建立方体和球体 */ + VdeBodyHandle box = vde_body_create_box(ctx, 2.0, 2.0, 2.0); + VdeBodyHandle sphere = vde_body_create_sphere(ctx, 1.2, 32, 32); + + /* 并集: box ∪ sphere */ + VdeBodyHandle union_body = vde_body_boolean(ctx, box, sphere, 0); + CHECK(union_body != NULL, "B-Rep 并集失败"); + VdeMeshHandle union_mesh = vde_body_to_mesh(ctx, union_body, 0.01); + printf(" 并集: %zu 顶点, %zu 面\n", + vde_mesh_num_vertices(union_mesh), + vde_mesh_num_faces(union_mesh)); + + /* 深拷贝演示 */ + VdeBodyHandle clone = vde_body_clone(ctx, union_body); + CHECK(clone != NULL, "vde_body_clone 失败"); + printf(" 深拷贝成功\n"); + + vde_mesh_free(union_mesh); + vde_body_free(clone); + vde_body_free(union_body); + vde_body_free(sphere); + vde_body_free(box); + vde_destroy(ctx); + printf(" B-Rep 布尔演示完成\n\n"); + return 0; +} + +/* ── 示例 7: SDF 隐式曲面 ──────────────────────────────── */ +static int demo07_sdf(void) { + printf("=== 示例 7: SDF 隐式曲面 ===\n"); + + VdeHandle ctx = vde_create(); + + /* 创建球体和盒子 SDF 节点 */ + VdeSdfHandle s1 = vde_sdf_sphere(ctx, 1.5); + VdeSdfHandle s2 = vde_sdf_box(ctx, 1.0, 1.0, 1.0); + + /* SDF 差集:球体减去盒子(得到一个带方形空腔的球) */ + VdeSdfHandle diff = vde_sdf_difference(ctx, s1, s2); + CHECK(diff != NULL, "SDF 差集失败"); + + /* 转为网格(注意:s1, s2 所有权已移交给 diff) */ + VdeMeshHandle mesh = vde_sdf_to_mesh(ctx, diff, 64); + if (mesh) { + printf(" SDF → Mesh: %zu 顶点, %zu 面 (resolution=64)\n", + vde_mesh_num_vertices(mesh), + vde_mesh_num_faces(mesh)); + vde_save_mesh(ctx, mesh, "/tmp/vde_capi_demo_sdf.obj", "obj"); + vde_mesh_free(mesh); + } + + /* 注意: s1, s2 已由 diff 拥有,只释放 diff */ + vde_sdf_free(diff); + vde_destroy(ctx); + printf(" SDF 演示完成\n\n"); + return 0; +} + +/* ── 示例 8: 序列化 ────────────────────────────────────── */ +static int demo08_serialize(void) { + printf("=== 示例 8: 序列化/反序列化 ===\n"); + + VdeHandle ctx = vde_create(); + + /* 创建网格 */ + VdeBodyHandle cyl = vde_body_create_cylinder(ctx, 1.0, 3.0, 24); + VdeMeshHandle mesh = vde_body_to_mesh(ctx, cyl, 0.01); + + printf(" 原始网格: %zu 顶点, %zu 面\n", + vde_mesh_num_vertices(mesh), + vde_mesh_num_faces(mesh)); + + /* 序列化 */ + uint8_t* data = NULL; + size_t len = vde_serialize_mesh(ctx, mesh, &data); + printf(" 序列化数据: %zu 字节\n", len); + + /* 反序列化 */ + VdeMeshHandle mesh2 = vde_deserialize_mesh(ctx, data, len); + if (mesh2) { + printf(" 反序列化网格: %zu 顶点, %zu 面\n", + vde_mesh_num_vertices(mesh2), + vde_mesh_num_faces(mesh2)); + vde_mesh_free(mesh2); + } + + /* 释放序列化缓冲区 */ + vde_free_buffer(data); + + vde_mesh_free(mesh); + vde_body_free(cyl); + vde_destroy(ctx); + printf(" 序列化演示完成\n\n"); + return 0; +} + +/* ── 示例 9: 错误处理 ──────────────────────────────────── */ +static void demo09_error_handling(void) { + printf("=== 示例 9: 错误处理 ===\n"); + + VdeHandle ctx = vde_create(); + + /* 尝试加载不存在的文件 */ + VdeMeshHandle bad = vde_load_mesh(ctx, "/nonexistent_file.obj"); + if (!bad) { + printf(" 加载失败 (预期): %s\n", vde_last_error(ctx)); + } + + vde_destroy(ctx); + printf(" 错误处理演示完成\n\n"); +} + +/* ── main ───────────────────────────────────────────────── */ +int main(void) { + printf("╔══════════════════════════════════════════╗\n"); + printf("║ ViewDesignEngine C API 基础使用示例 ║\n"); + printf("╚══════════════════════════════════════════╝\n\n"); + + demo01_lifecycle(); + + int ret = 0; + ret |= demo02_mesh_io(); + ret |= demo03_mesh_process(); + ret |= demo04_collision(); + ret |= demo05_curve(); + ret |= demo06_brep_boolean(); + ret |= demo07_sdf(); + ret |= demo08_serialize(); + demo09_error_handling(); + + if (ret == 0) { + printf("所有演示通过 ✓\n"); + } else { + printf("部分演示失败 ✗\n"); + } + return ret; +} diff --git a/include/vde/capi/vde_capi.h b/include/vde/capi/vde_capi.h index 51bf960..005385f 100644 --- a/include/vde/capi/vde_capi.h +++ b/include/vde/capi/vde_capi.h @@ -588,31 +588,162 @@ VdeMeshHandle vde_body_to_mesh(VdeHandle ctx, VdeBodyHandle body, double deflect */ void vde_body_free(VdeBodyHandle body); -/** @brief 深拷贝 B-Rep 实体 */ +/** + * @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 实体列表。返回数量,每个 body 写入 out_bodies */ +/** + * @brief 从 STEP 文件导入 B-Rep 实体列表 + * + * STEP(ISO 10303)是工业标准的 CAD 数据交换格式。 + * 一个 STEP 文件可能包含多个实体,所有实体同时加载。 + * + * @param ctx 引擎上下文句柄 + * @param path STEP 文件路径(UTF-8 编码) + * @param out_bodies [out] 指向实体句柄数组的指针,由库分配,调用方通过 vde_body_free_array() 释放 + * @return 导入的实体数量,失败返回 0(通过 vde_last_error() 获取原因) + * + * @warning 若返回 0,out_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 实体列表 */ +/** + * @brief 从 IGES 文件导入 B-Rep 实体列表 + * + * IGES(Initial 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 文件。成功返回 1 */ +/** + * @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 文件 */ +/** + * @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 布尔运算。op: 0=Union, 1=Intersection, 2=Difference (A\B) */ +/** + * @brief B-Rep 实体布尔运算 + * + * 对两个边界表示实体执行精确布尔运算(基于 Parasolid 风格的边界求交)。 + * 与网格布尔运算相比,B-Rep 布尔运算结果更精确,适合工程应用。 + * + * @param ctx 引擎上下文句柄 + * @param a 第一个 B-Rep 实体句柄 + * @param b 第二个 B-Rep 实体句柄 + * @param op 布尔操作类型: 0 = 并集(Union),1 = 交集(Intersection),2 = 差集(Difference,A \\ 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 获取网格面的顶点索引。返回顶点数,out_indices 需调用 vde_free_buffer 释放 */ +/** + * @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); // ═══════════════════════════════════════════════════════════ @@ -624,28 +755,146 @@ int vde_mesh_get_face(VdeMeshHandle mesh, size_t face_idx, int** out_indices); typedef struct VdeSdfNode* VdeSdfHandle; #endif -/** @brief 创建 SDF 球体节点 */ +/** + * @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 盒子节点 */ +/** + * @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 圆柱节点 */ +/** + * @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 布尔并集 */ +/** + * @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 布尔交集 */ +/** + * @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 布尔差集 */ +/** + * @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 转网格 */ +/** + * @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 节点 */ +/** + * @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); // ═══════════════════════════════════════════════════════════ @@ -657,19 +906,90 @@ void vde_sdf_free(VdeSdfHandle sdf); typedef struct VdeAssembly* VdeAssemblyHandle; #endif -/** @brief 创建装配体 */ +/** + * @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 向装配体根节点添加零件 */ +/** + * @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 获取装配体零件数量 */ +/** + * @brief 获取装配体零件数量 + * + * @param assembly 装配体句柄 + * @return 零件总数 + * + * @see vde_assembly_add_part + * @ingroup capi + */ int vde_assembly_part_count(VdeAssemblyHandle assembly); -/** @brief 释放装配体(不释放其中的 body,调用方自行管理) */ +/** + * @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 句柄数组(由 load_step/load_iges 返回) */ +/** + * @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); // ═══════════════════════════════════════════════════════════ diff --git a/include/vde/core/transform.h b/include/vde/core/transform.h index 3fe8dd0..f8c176f 100644 --- a/include/vde/core/transform.h +++ b/include/vde/core/transform.h @@ -1,4 +1,5 @@ #pragma once +#include "vde/core/plane.h" #include "vde/core/point.h" #include @@ -119,4 +120,67 @@ inline Transform3D scale(double sx, double sy, double sz) { */ inline Transform3D scale(double s) { return scale(s, s, s); } +/** + * @brief 创建关于过原点平面的镜面反射变换 + * + * 反射矩阵为 \f$ M = I - 2 \cdot \hat{n} \hat{n}^T \f$, + * 其中 \f$ \hat{n} \f$ 是单位法向量。 + * + * @param normal 平面法向量(自动归一化) + * @return 镜面反射变换矩阵 + * + * @code{.cpp} + * // 关于 YZ 平面(法向量 X 轴)反射 + * auto R = mirror(Vector3D::UnitX()); + * Point3D p2 = R * Point3D(3, 1, 0); // p2 == (-3, 1, 0) + * @endcode + * + * @note 反射平面过原点,不能处理一般位置平面。使用 mirror_about_plane() 处理任意平面。 + * + * @see mirror_about_plane + * @ingroup core + */ +inline Transform3D mirror(const Vector3D& normal) { + Vector3D n = normal.normalized(); + Eigen::Matrix3d refl = Eigen::Matrix3d::Identity() - 2.0 * n * n.transpose(); + Transform3D T; + T.linear() = refl; + T.translation().setZero(); + return T; +} + +/** + * @brief 创建关于任意平面的镜面反射变换 + * + * 一般位置平面方程为 \f$ \hat{n} \cdot x + d = 0 \f$, + * 反射变换为: + * \f[ + * T = \begin{bmatrix} I - 2\hat{n}\hat{n}^T & -2d\hat{n} \\ 0 & 1 \end{bmatrix} + * \f] + * + * @param plane 反射平面(法向量自动归一化) + * @return 镜面反射变换矩阵 + * + * @code{.cpp} + * Plane3D plane(Point3D(1, 0, 0), Vector3D::UnitX()); + * auto R = mirror_about_plane(plane); + * // 点 (3, 0, 0) 到平面距离为 2,反射后变为 (-1, 0, 0) + * Point3D p2 = R * Point3D(3, 0, 0); + * @endcode + * + * @note 平面法向量在构造 Plane3D 时已归一化,无需额外处理。 + * + * @see mirror + * @ingroup core + */ +inline Transform3D mirror_about_plane(const Plane3D& plane) { + const Vector3D& n = plane.normal(); + double d = plane.d(); + Eigen::Matrix3d refl = Eigen::Matrix3d::Identity() - 2.0 * n * n.transpose(); + Transform3D T; + T.linear() = refl; + T.translation() = -2.0 * d * n; + return T; +} + } // namespace vde::core diff --git a/include/vde/foundation/io_stl.h b/include/vde/foundation/io_stl.h index f619ef4..82d0d7b 100644 --- a/include/vde/foundation/io_stl.h +++ b/include/vde/foundation/io_stl.h @@ -11,6 +11,9 @@ namespace vde::foundation { * 表示一个 STL 三角形,包含法向量和三个顶点。 * 法向量应为单位向量,方向朝外(右手法则)。 * + * 除直接访问成员(v0, v1, v2)外,提供便捷分量访问方法, + * 无需通过 Point3D 的 .x()/.y()/.z() 间接获取。 + * * @ingroup foundation */ struct StlTriangle { @@ -18,6 +21,30 @@ struct StlTriangle { Vector3D normal; /// 三个顶点(右手法则 CCW 顺序) Point3D v0, v1, v2; + + // ── 便捷分量访问:顶点 v0 ── + /// 顶点 v0 的 X 分量 + [[nodiscard]] double v0x() const { return v0.x(); } + /// 顶点 v0 的 Y 分量 + [[nodiscard]] double v0y() const { return v0.y(); } + /// 顶点 v0 的 Z 分量 + [[nodiscard]] double v0z() const { return v0.z(); } + + // ── 便捷分量访问:顶点 v1 ── + /// 顶点 v1 的 X 分量 + [[nodiscard]] double v1x() const { return v1.x(); } + /// 顶点 v1 的 Y 分量 + [[nodiscard]] double v1y() const { return v1.y(); } + /// 顶点 v1 的 Z 分量 + [[nodiscard]] double v1z() const { return v1.z(); } + + // ── 便捷分量访问:顶点 v2 ── + /// 顶点 v2 的 X 分量 + [[nodiscard]] double v2x() const { return v2.x(); } + /// 顶点 v2 的 Y 分量 + [[nodiscard]] double v2y() const { return v2.y(); } + /// 顶点 v2 的 Z 分量 + [[nodiscard]] double v2z() const { return v2.z(); } }; /** diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 858753c..9d3ffcf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,7 +15,7 @@ add_library(vde_foundation STATIC foundation/error_codes.cpp ) target_include_directories(vde_foundation - PUBLIC ${CMAKE_SOURCE_DIR}/include + PUBLIC $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) target_link_libraries(vde_foundation @@ -42,7 +42,7 @@ add_library(vde_core STATIC core/license_manager.cpp ) target_include_directories(vde_core - PUBLIC ${CMAKE_SOURCE_DIR}/include + PUBLIC $ PUBLIC ${CMAKE_BINARY_DIR}/generated PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) @@ -77,7 +77,7 @@ add_library(vde_curves STATIC curves/generative_surfaces.cpp ) target_include_directories(vde_curves - PUBLIC ${CMAKE_SOURCE_DIR}/include + PUBLIC $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) target_link_libraries(vde_curves @@ -106,9 +106,10 @@ add_library(vde_mesh STATIC mesh/point_cloud.cpp mesh/fea_mesh.cpp mesh/visualization_quality.cpp + mesh/brep_to_mesh.cpp ) target_include_directories(vde_mesh - PUBLIC ${CMAKE_SOURCE_DIR}/include + PUBLIC $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) target_link_libraries(vde_mesh @@ -124,7 +125,7 @@ add_library(vde_spatial STATIC spatial/incremental_bvh.cpp ) target_include_directories(vde_spatial - PUBLIC ${CMAKE_SOURCE_DIR}/include + PUBLIC $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) target_link_libraries(vde_spatial @@ -138,7 +139,7 @@ add_library(vde_boolean STATIC boolean/polygon_offset.cpp ) target_include_directories(vde_boolean - PUBLIC ${CMAKE_SOURCE_DIR}/include + PUBLIC $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) target_link_libraries(vde_boolean @@ -153,7 +154,7 @@ add_library(vde_collision STATIC collision/tri_intersect.cpp ) target_include_directories(vde_collision - PUBLIC ${CMAKE_SOURCE_DIR}/include + PUBLIC $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) target_link_libraries(vde_collision @@ -229,11 +230,11 @@ add_library(vde_brep STATIC brep/quality_feedback.cpp ) target_include_directories(vde_brep - PUBLIC ${CMAKE_SOURCE_DIR}/include + PUBLIC $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) target_link_libraries(vde_brep - PUBLIC vde_curves vde_mesh vde_collision vde_compile_options + PUBLIC vde_curves vde_collision vde_compile_options ) # ── sketch ───────────────────────────────────────── @@ -241,7 +242,7 @@ add_library(vde_sketch STATIC sketch/constraint_solver.cpp ) target_include_directories(vde_sketch - PUBLIC ${CMAKE_SOURCE_DIR}/include + PUBLIC $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) target_link_libraries(vde_sketch @@ -257,7 +258,7 @@ add_library(vde_sdf STATIC sdf/sdf_optimize.cpp ) target_include_directories(vde_sdf - PUBLIC ${CMAKE_SOURCE_DIR}/include + PUBLIC $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) target_link_libraries(vde_sdf @@ -290,7 +291,7 @@ add_library(vde_capi STATIC capi/vde_capi.cpp ) target_include_directories(vde_capi - PUBLIC ${CMAKE_SOURCE_DIR}/include + PUBLIC $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) target_link_libraries(vde_capi diff --git a/src/brep/advanced_blend.cpp b/src/brep/advanced_blend.cpp index 6d457e6..16b1f8a 100644 --- a/src/brep/advanced_blend.cpp +++ b/src/brep/advanced_blend.cpp @@ -10,6 +10,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { namespace { diff --git a/src/brep/advanced_healing.cpp b/src/brep/advanced_healing.cpp index 2e69e76..142bfbc 100644 --- a/src/brep/advanced_healing.cpp +++ b/src/brep/advanced_healing.cpp @@ -13,6 +13,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/assembly_constraints.cpp b/src/brep/assembly_constraints.cpp index 19aff9a..db4ff08 100644 --- a/src/brep/assembly_constraints.cpp +++ b/src/brep/assembly_constraints.cpp @@ -4,6 +4,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { namespace { diff --git a/src/brep/assembly_feature.cpp b/src/brep/assembly_feature.cpp index 32bb7b5..bfb5b1b 100644 --- a/src/brep/assembly_feature.cpp +++ b/src/brep/assembly_feature.cpp @@ -5,6 +5,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { // ═══════════════════════════════════════════════════════════════════════════ diff --git a/src/brep/assembly_instance.cpp b/src/brep/assembly_instance.cpp index 99813a2..41bc2d4 100644 --- a/src/brep/assembly_instance.cpp +++ b/src/brep/assembly_instance.cpp @@ -1,5 +1,12 @@ #include "vde/brep/assembly_instance.h" +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { int AssemblyInstancer::register_instance(const std::string& name, const BrepModel& geom) { diff --git a/src/brep/assembly_lod.cpp b/src/brep/assembly_lod.cpp index 6bc0f52..f2ab11a 100644 --- a/src/brep/assembly_lod.cpp +++ b/src/brep/assembly_lod.cpp @@ -3,6 +3,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { // ═══════════════════════════════════════════════════════════ diff --git a/src/brep/assembly_patterns.cpp b/src/brep/assembly_patterns.cpp index 491f4fb..ddc5301 100644 --- a/src/brep/assembly_patterns.cpp +++ b/src/brep/assembly_patterns.cpp @@ -6,6 +6,13 @@ #define M_PI 3.14159265358979323846 #endif +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { namespace { diff --git a/src/brep/auto_dimensioning.cpp b/src/brep/auto_dimensioning.cpp index ad78865..27e73d8 100644 --- a/src/brep/auto_dimensioning.cpp +++ b/src/brep/auto_dimensioning.cpp @@ -8,6 +8,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/boolean_fallback.cpp b/src/brep/boolean_fallback.cpp index 9ebf22b..f2b4f49 100644 --- a/src/brep/boolean_fallback.cpp +++ b/src/brep/boolean_fallback.cpp @@ -12,6 +12,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using vde::mesh::BooleanOp; diff --git a/src/brep/brep.cpp b/src/brep/brep.cpp index cee100c..76d7120 100644 --- a/src/brep/brep.cpp +++ b/src/brep/brep.cpp @@ -2,6 +2,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { int BrepModel::add_vertex(const Point3D& p) { @@ -103,56 +110,6 @@ bool BrepModel::is_valid() const { return true; } -mesh::HalfedgeMesh BrepModel::to_mesh(double deflection) const { - mesh::HalfedgeMesh result; - std::vector all_verts; - std::vector> all_tris; - - for (const auto& face : faces_) { - // Prefer TrimmedSurface if available - if (face.trimmed_surface_id >= 0 && - face.trimmed_surface_id < static_cast(trimmed_surfaces_.size())) { - const auto& ts = trimmed_surfaces_[face.trimmed_surface_id]; - auto ts_mesh = ts.to_mesh(deflection); - - // Merge vertices and faces from trimmed surface mesh - std::vector ts_verts; - std::vector> ts_tris; - for (size_t vi = 0; vi < ts_mesh.num_vertices(); ++vi) - ts_verts.push_back(ts_mesh.vertex(vi)); - for (size_t fi = 0; fi < ts_mesh.num_faces(); ++fi) { - auto fv = ts_mesh.face_vertices(static_cast(fi)); - if (fv.size() >= 3) { - for (size_t ti = 0; ti + 2 < fv.size(); ++ti) - ts_tris.push_back({fv[0], fv[ti+1], fv[ti+2]}); - } - } - - int offset = static_cast(all_verts.size()); - for (const auto& v : ts_verts) all_verts.push_back(v); - for (const auto& t : ts_tris) - all_tris.push_back({t[0]+offset, t[1]+offset, t[2]+offset}); - continue; - } - - // Fall back to base surface tessellation - if (face.surface_id < 0 || face.surface_id >= static_cast(surfaces_.size())) - continue; - const auto& surf = surfaces_[face.surface_id]; - int res = std::max(4, static_cast(1.0 / deflection)); - auto [verts, tris] = surf.tessellate(res, res); - - int offset = static_cast(all_verts.size()); - for (const auto& v : verts) all_verts.push_back(v); - for (const auto& t : tris) - all_tris.push_back({t[0]+offset, t[1]+offset, t[2]+offset}); - } - - if (!all_verts.empty()) - result.build_from_triangles(all_verts, all_tris); - return result; -} - std::vector BrepModel::face_edges(int face_id) const { std::vector result; if (face_id < 0 || face_id >= static_cast(faces_.size())) return result; diff --git a/src/brep/brep_boolean.cpp b/src/brep/brep_boolean.cpp index 38d9087..6a0e089 100644 --- a/src/brep/brep_boolean.cpp +++ b/src/brep/brep_boolean.cpp @@ -19,6 +19,13 @@ #include #endif +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/brep_drawing.cpp b/src/brep/brep_drawing.cpp index d9c0a2a..00c3ae8 100644 --- a/src/brep/brep_drawing.cpp +++ b/src/brep/brep_drawing.cpp @@ -6,6 +6,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; using core::Vector3D; diff --git a/src/brep/brep_face_split.cpp b/src/brep/brep_face_split.cpp index d511f4c..1e6d711 100644 --- a/src/brep/brep_face_split.cpp +++ b/src/brep/brep_face_split.cpp @@ -4,6 +4,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/brep_heal.cpp b/src/brep/brep_heal.cpp index f15a538..a517d75 100644 --- a/src/brep/brep_heal.cpp +++ b/src/brep/brep_heal.cpp @@ -6,6 +6,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/brep_validate.cpp b/src/brep/brep_validate.cpp index 7c09839..8033de7 100644 --- a/src/brep/brep_validate.cpp +++ b/src/brep/brep_validate.cpp @@ -6,6 +6,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/constraint_solver.cpp b/src/brep/constraint_solver.cpp index c2c1203..ad30c31 100644 --- a/src/brep/constraint_solver.cpp +++ b/src/brep/constraint_solver.cpp @@ -10,6 +10,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { // ═══════════════════════════════════════════════════════════ diff --git a/src/brep/defeature.cpp b/src/brep/defeature.cpp index 355964e..1bb2e6a 100644 --- a/src/brep/defeature.cpp +++ b/src/brep/defeature.cpp @@ -1,4 +1,11 @@ #include "vde/brep/defeature.h" +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { BrepModel defeature(const BrepModel& body, const std::vector& features) { BrepModel result=body; diff --git a/src/brep/direct_modeling.cpp b/src/brep/direct_modeling.cpp index 7c23fae..7803bd5 100644 --- a/src/brep/direct_modeling.cpp +++ b/src/brep/direct_modeling.cpp @@ -9,6 +9,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/draft_analysis.cpp b/src/brep/draft_analysis.cpp index 3ad83db..de3c6ac 100644 --- a/src/brep/draft_analysis.cpp +++ b/src/brep/draft_analysis.cpp @@ -4,6 +4,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { // ═══════════════════════════════════════════════════════════ diff --git a/src/brep/drawing_standards.cpp b/src/brep/drawing_standards.cpp index cf9b88b..67f9c48 100644 --- a/src/brep/drawing_standards.cpp +++ b/src/brep/drawing_standards.cpp @@ -3,6 +3,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { // ═══════════════════════════════════════════════════════════ diff --git a/src/brep/dxf_import.cpp b/src/brep/dxf_import.cpp index 1e88d3a..cfcb33d 100644 --- a/src/brep/dxf_import.cpp +++ b/src/brep/dxf_import.cpp @@ -8,6 +8,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/euler_op.cpp b/src/brep/euler_op.cpp index 65b3894..fe2a834 100644 --- a/src/brep/euler_op.cpp +++ b/src/brep/euler_op.cpp @@ -4,6 +4,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/explode_view.cpp b/src/brep/explode_view.cpp index 1f26b3e..3b7007e 100644 --- a/src/brep/explode_view.cpp +++ b/src/brep/explode_view.cpp @@ -4,6 +4,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/feature_recognition.cpp b/src/brep/feature_recognition.cpp index dd9924b..818164d 100644 --- a/src/brep/feature_recognition.cpp +++ b/src/brep/feature_recognition.cpp @@ -1,6 +1,13 @@ #include "vde/brep/feature_recognition.h" #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { namespace { bool is_cylindrical_face(const BrepModel& body, int fid, double& radius, core::Vector3D& axis) { diff --git a/src/brep/feature_tree.cpp b/src/brep/feature_tree.cpp index 1ccabc0..6abaa88 100644 --- a/src/brep/feature_tree.cpp +++ b/src/brep/feature_tree.cpp @@ -3,6 +3,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { BrepModel FeatureNode::evaluate() const { diff --git a/src/brep/ffd_deformation.cpp b/src/brep/ffd_deformation.cpp index c04923b..c0039a3 100644 --- a/src/brep/ffd_deformation.cpp +++ b/src/brep/ffd_deformation.cpp @@ -3,6 +3,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { // ═══════════════════════════════════════════════ diff --git a/src/brep/flexible_assembly.cpp b/src/brep/flexible_assembly.cpp index 4480928..19c79d3 100644 --- a/src/brep/flexible_assembly.cpp +++ b/src/brep/flexible_assembly.cpp @@ -1,4 +1,11 @@ #include "vde/brep/flexible_assembly.h" +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { void create_flexible_assembly(Assembly& assembly, const std::vector& parts, const std::vector& deformable_ids) { (void)assembly;(void)parts;(void)deformable_ids; } } // namespace vde::brep diff --git a/src/brep/format_io.cpp b/src/brep/format_io.cpp index 3347afd..c05a25b 100644 --- a/src/brep/format_io.cpp +++ b/src/brep/format_io.cpp @@ -6,6 +6,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { // ═══════════════════════════════════════════════════════════ diff --git a/src/brep/gdt.cpp b/src/brep/gdt.cpp index cd88cdc..308e362 100644 --- a/src/brep/gdt.cpp +++ b/src/brep/gdt.cpp @@ -5,6 +5,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/iges_export.cpp b/src/brep/iges_export.cpp index a8396f9..a11d356 100644 --- a/src/brep/iges_export.cpp +++ b/src/brep/iges_export.cpp @@ -7,6 +7,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using namespace vde::foundation; diff --git a/src/brep/iges_import.cpp b/src/brep/iges_import.cpp index 82953fc..bcea573 100644 --- a/src/brep/iges_import.cpp +++ b/src/brep/iges_import.cpp @@ -12,6 +12,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/incremental_mesh.cpp b/src/brep/incremental_mesh.cpp index 4ea08e6..5300b3b 100644 --- a/src/brep/incremental_mesh.cpp +++ b/src/brep/incremental_mesh.cpp @@ -3,6 +3,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { // ═══════════════════════════════════════════════════════════ diff --git a/src/brep/incremental_update.cpp b/src/brep/incremental_update.cpp index 45e5c9a..fc67709 100644 --- a/src/brep/incremental_update.cpp +++ b/src/brep/incremental_update.cpp @@ -3,6 +3,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { void IncrementalUpdateEngine::register_node(int node_id, const std::vector& deps) { diff --git a/src/brep/interference_check.cpp b/src/brep/interference_check.cpp index 4023aa7..67bdef9 100644 --- a/src/brep/interference_check.cpp +++ b/src/brep/interference_check.cpp @@ -7,6 +7,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/kinematic_chain.cpp b/src/brep/kinematic_chain.cpp index 4000b96..af7afd6 100644 --- a/src/brep/kinematic_chain.cpp +++ b/src/brep/kinematic_chain.cpp @@ -3,6 +3,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { // ═══════════════════════════════════════════════════════════ diff --git a/src/brep/large_assembly.cpp b/src/brep/large_assembly.cpp index ba4ab22..3559811 100644 --- a/src/brep/large_assembly.cpp +++ b/src/brep/large_assembly.cpp @@ -2,6 +2,13 @@ #include "vde/core/aabb.h" #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/measure.cpp b/src/brep/measure.cpp index 175692a..0e26170 100644 --- a/src/brep/measure.cpp +++ b/src/brep/measure.cpp @@ -10,6 +10,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/modeling.cpp b/src/brep/modeling.cpp index 04afeae..36bfcf5 100644 --- a/src/brep/modeling.cpp +++ b/src/brep/modeling.cpp @@ -5,6 +5,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { namespace { diff --git a/src/brep/motion_simulation.cpp b/src/brep/motion_simulation.cpp index 73eabdf..f85daee 100644 --- a/src/brep/motion_simulation.cpp +++ b/src/brep/motion_simulation.cpp @@ -4,6 +4,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/parallel_boolean.cpp b/src/brep/parallel_boolean.cpp index 261ecb4..eb86258 100644 --- a/src/brep/parallel_boolean.cpp +++ b/src/brep/parallel_boolean.cpp @@ -12,6 +12,13 @@ #include #endif +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { // Forward declaration diff --git a/src/brep/pmi_mbd.cpp b/src/brep/pmi_mbd.cpp index 6a3849b..e61c804 100644 --- a/src/brep/pmi_mbd.cpp +++ b/src/brep/pmi_mbd.cpp @@ -6,6 +6,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/quality_feedback.cpp b/src/brep/quality_feedback.cpp index f1aad94..80e5492 100644 --- a/src/brep/quality_feedback.cpp +++ b/src/brep/quality_feedback.cpp @@ -10,6 +10,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { // ═══════════════════════════════════════════════════════════ diff --git a/src/brep/sheet_metal.cpp b/src/brep/sheet_metal.cpp index 95a6db9..c9b5cae 100644 --- a/src/brep/sheet_metal.cpp +++ b/src/brep/sheet_metal.cpp @@ -12,6 +12,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/ssi_boolean.cpp b/src/brep/ssi_boolean.cpp index f90b7f4..d200c0e 100644 --- a/src/brep/ssi_boolean.cpp +++ b/src/brep/ssi_boolean.cpp @@ -23,6 +23,13 @@ #include #endif +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/step_export.cpp b/src/brep/step_export.cpp index 0970ce7..dfc029b 100644 --- a/src/brep/step_export.cpp +++ b/src/brep/step_export.cpp @@ -6,6 +6,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { namespace { diff --git a/src/brep/step_import.cpp b/src/brep/step_import.cpp index a412797..cbcb34f 100644 --- a/src/brep/step_import.cpp +++ b/src/brep/step_import.cpp @@ -13,6 +13,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/tolerance.cpp b/src/brep/tolerance.cpp index b2b37b7..4b1c30a 100644 --- a/src/brep/tolerance.cpp +++ b/src/brep/tolerance.cpp @@ -3,6 +3,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { // Global tolerance config diff --git a/src/brep/tolerant_modeling.cpp b/src/brep/tolerant_modeling.cpp index 7729474..833f7b9 100644 --- a/src/brep/tolerant_modeling.cpp +++ b/src/brep/tolerant_modeling.cpp @@ -14,6 +14,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { using core::Point3D; diff --git a/src/brep/trimmed_surface.cpp b/src/brep/trimmed_surface.cpp index 32c889e..8a0877d 100644 --- a/src/brep/trimmed_surface.cpp +++ b/src/brep/trimmed_surface.cpp @@ -6,6 +6,13 @@ #include #include +using vde::core::AABB3D; +using vde::core::Point2D; +using vde::core::Point3D; +using vde::core::Vector2D; +using vde::core::Vector3D; +using vde::core::Transform3D; +using vde::core::Plane3D; namespace vde::brep { // ───────────────────────────────────────────────── diff --git a/src/core/performance_tuning.cpp b/src/core/performance_tuning.cpp index 01da999..b9e966c 100644 --- a/src/core/performance_tuning.cpp +++ b/src/core/performance_tuning.cpp @@ -31,6 +31,28 @@ int hardware_concurrency() { return n > 0 ? n : 4; } +/// 跨平台对齐内存分配 +/// - POSIX: posix_memalign +/// - Windows/MinGW: _aligned_malloc +inline void* aligned_allocate(size_t alignment, size_t size) { +#ifdef _WIN32 + return _aligned_malloc(size, alignment); +#else + void* ptr = nullptr; + if (posix_memalign(&ptr, alignment, size) != 0) return nullptr; + return ptr; +#endif +} + +/// 跨平台对齐内存释放 +inline void aligned_deallocate(void* ptr) { +#ifdef _WIN32 + _aligned_free(ptr); +#else + free(ptr); +#endif +} + /// 拓扑排序(Kahn 算法) std::vector topological_sort(const std::vector& tasks) { std::unordered_map in_degree; diff --git a/src/foundation/io_3mf.cpp b/src/foundation/io_3mf.cpp index 51e7905..af62b6c 100644 --- a/src/foundation/io_3mf.cpp +++ b/src/foundation/io_3mf.cpp @@ -159,7 +159,7 @@ std::map read_zip(const std::string& path) { // Get file size file.seekg(0, std::ios::end); - auto fsize = file.tellg(); + std::streamoff fsize = file.tellg(); file.seekg(0, std::ios::beg); // Find EOCD signature backwards from end of file @@ -167,7 +167,7 @@ std::map read_zip(const std::string& path) { // We search from end back up to 64KB (max comment length) uint32_t eocd_offset = 0; bool found = false; - auto search_start = std::max(0, fsize - 65557); + std::streamoff search_start = std::max(0, fsize - 65557); file.seekg(search_start, std::ios::beg); std::vector tail(static_cast(fsize - search_start)); file.read(reinterpret_cast(tail.data()), tail.size()); diff --git a/src/mesh/brep_to_mesh.cpp b/src/mesh/brep_to_mesh.cpp new file mode 100644 index 0000000..4da635d --- /dev/null +++ b/src/mesh/brep_to_mesh.cpp @@ -0,0 +1,57 @@ +#include "vde/brep/brep.h" +#include "vde/mesh/halfedge_mesh.h" +#include + +namespace vde::brep { + +mesh::HalfedgeMesh BrepModel::to_mesh(double deflection) const { + mesh::HalfedgeMesh result; + std::vector all_verts; + std::vector> all_tris; + + for (const auto& face : faces_) { + // Prefer TrimmedSurface if available + if (face.trimmed_surface_id >= 0 && + face.trimmed_surface_id < static_cast(trimmed_surfaces_.size())) { + const auto& ts = trimmed_surfaces_[face.trimmed_surface_id]; + auto ts_mesh = ts.to_mesh(deflection); + + // Merge vertices and faces from trimmed surface mesh + std::vector ts_verts; + std::vector> ts_tris; + for (size_t vi = 0; vi < ts_mesh.num_vertices(); ++vi) + ts_verts.push_back(ts_mesh.vertex(vi)); + for (size_t fi = 0; fi < ts_mesh.num_faces(); ++fi) { + auto fv = ts_mesh.face_vertices(static_cast(fi)); + if (fv.size() >= 3) { + for (size_t ti = 0; ti + 2 < fv.size(); ++ti) + ts_tris.push_back({fv[0], fv[ti+1], fv[ti+2]}); + } + } + + int offset = static_cast(all_verts.size()); + for (const auto& v : ts_verts) all_verts.push_back(v); + for (const auto& t : ts_tris) + all_tris.push_back({t[0]+offset, t[1]+offset, t[2]+offset}); + continue; + } + + // Fall back to base surface tessellation + if (face.surface_id < 0 || face.surface_id >= static_cast(surfaces_.size())) + continue; + const auto& surf = surfaces_[face.surface_id]; + int res = std::max(4, static_cast(1.0 / deflection)); + auto [verts, tris] = surf.tessellate(res, res); + + int offset = static_cast(all_verts.size()); + for (const auto& v : verts) all_verts.push_back(v); + for (const auto& t : tris) + all_tris.push_back({t[0]+offset, t[1]+offset, t[2]+offset}); + } + + if (!all_verts.empty()) + result.build_from_triangles(all_verts, all_tris); + return result; +} + +} // namespace vde::brep diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 47a0581..9d1c65e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,7 +1,7 @@ function(add_vde_test name) add_executable(${name} ${name}.cpp) target_link_libraries(${name} PRIVATE vde GTest::gtest GTest::gtest_main) - target_include_directories(${name} PRIVATE ${CMAKE_SOURCE_DIR}/include) + target_include_directories(${name} PRIVATE $) gtest_discover_tests(${name}) endfunction()