fix(v1.0.1): VDE-002 submodule path + VDE-006 cycle dep + VDE-007/008/009 API fixes
CI / Build & Test (push) Failing after 1m31s
CI / Release Build (push) Failing after 32s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled

VDE-002 (High): Fix submodule include path
- All target_include_directories: CMAKE_SOURCE_DIR → BUILD_INTERFACE
- Works as both standalone project and add_subdirectory()

VDE-006 (High): Break vde_mesh ↔ vde_brep circular dependency
- Move mesh-dependent code from brep to mesh module
- vde_brep no longer links vde_mesh

VDE-007 (Low): Add mirror() and mirror_about_plane() to transform.h

VDE-008 (Low): Add convenience accessors to StlTriangle (.x()/.y()/.z())

VDE-009 (Medium): Full Doxygen docs for C API + examples/capi/basic_usage.cpp

Pending: VDE-001/003/004/005/010 GCC compatibility (agent still running)
This commit is contained in:
茂之钳
2026-07-27 22:29:06 +08:00
parent 5bfcbcb8e9
commit a73c5e8954
60 changed files with 1222 additions and 85 deletions
+1
View File
@@ -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)
+2
View File
@@ -0,0 +1,2 @@
add_executable(basic_usage basic_usage.cpp)
target_link_libraries(basic_usage PRIVATE vde)
+350
View File
@@ -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 <stdio.h>
#include <stdlib.h>
#include <math.h>
/* 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;
}