feat: 插件化 API 架构 — C API + 模块独立 + 外部集成示例
核心设计: - include/vde/vde.h: 统一 C API(50+ 函数,6 种不透明句柄) - 上下文管理 / I/O / Mesh / 曲线 / 碰撞 / 约束求解 / B-Rep / 序列化 - src/capi/vde_capi.cpp: 完整 C API 实现 (240行) - examples/07_capi_integration: C 语言外部调用示例 - CMake: 添加 vde_capi 库 + install 导出支持 模块架构(每个独立 .a): vde_foundation → vde_core → vde_curves → vde_mesh → vde_spatial → vde_boolean / vde_collision → vde_brep / vde_sketch → vde_capi
This commit is contained in:
@@ -161,3 +161,16 @@ target_include_directories(vde_sketch
|
||||
target_link_libraries(vde_sketch
|
||||
PUBLIC vde_core vde_compile_options
|
||||
)
|
||||
|
||||
# ── C API ──────────────────────────────────────────
|
||||
add_library(vde_capi STATIC
|
||||
capi/vde_capi.cpp
|
||||
)
|
||||
target_include_directories(vde_capi
|
||||
PUBLIC ${CMAKE_SOURCE_DIR}/include
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
target_link_libraries(vde_capi
|
||||
PUBLIC vde_brep vde_sketch vde_collision vde_boolean vde_spatial
|
||||
PUBLIC vde_mesh vde_curves vde_core vde_foundation vde_compile_options
|
||||
)
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
#include "vde/vde.h"
|
||||
#include "vde/core/point.h"
|
||||
#include "vde/mesh/halfedge_mesh.h"
|
||||
#include "vde/mesh/mesh_simplify.h"
|
||||
#include "vde/mesh/mesh_smooth.h"
|
||||
#include "vde/mesh/mesh_boolean.h"
|
||||
#include "vde/curves/bezier_curve.h"
|
||||
#include "vde/curves/nurbs_curve.h"
|
||||
#include "vde/collision/gjk.h"
|
||||
#include "vde/collision/ray_intersect.h"
|
||||
#include "vde/sketch/constraint_solver.h"
|
||||
#include "vde/brep/modeling.h"
|
||||
#include "vde/brep/brep.h"
|
||||
#include "vde/spatial/bvh.h"
|
||||
#include "vde/foundation/serializer.h"
|
||||
#include "vde/foundation/io_obj.h"
|
||||
#include "vde/foundation/io_stl.h"
|
||||
#include "vde/foundation/io_ply.h"
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
namespace {
|
||||
using namespace vde;
|
||||
using core::Point3D;
|
||||
using core::Vector3D;
|
||||
using core::Triangle3D;
|
||||
using core::Ray3Dd;
|
||||
using mesh::HalfedgeMesh;
|
||||
using mesh::BooleanOp;
|
||||
using curves::BezierCurve;
|
||||
using curves::NurbsCurve;
|
||||
}
|
||||
|
||||
static bool str_ends_with(const std::string& s, const std::string& suffix) {
|
||||
return s.size() >= suffix.size() &&
|
||||
s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0;
|
||||
}
|
||||
|
||||
struct VdeContext {
|
||||
std::string last_error;
|
||||
int next_id = 1;
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
VdeHandle vde_create(void) { return reinterpret_cast<VdeHandle>(new VdeContext()); }
|
||||
void vde_destroy(VdeHandle h) { delete reinterpret_cast<VdeContext*>(h); }
|
||||
const char* vde_version(void) { return "0.5.0"; }
|
||||
const char* vde_last_error(VdeHandle h) { return reinterpret_cast<VdeContext*>(h)->last_error.c_str(); }
|
||||
|
||||
// ── I/O ──
|
||||
VdeMeshHandle vde_load_mesh(VdeHandle, const char* path) {
|
||||
std::string p(path);
|
||||
auto* m = new HalfedgeMesh();
|
||||
if (str_ends_with(p, ".obj")) {
|
||||
auto data = foundation::read_obj(path);
|
||||
std::vector<std::array<int,3>> tris;
|
||||
for (auto& f : data.faces) if (f.size()>=3) tris.push_back({f[0],f[1],f[2]});
|
||||
m->build_from_triangles(data.vertices, tris);
|
||||
} else if (str_ends_with(p, ".stl")) {
|
||||
auto stl_tris = foundation::read_stl(path);
|
||||
std::vector<Point3D> verts;
|
||||
std::vector<std::array<int,3>> idx;
|
||||
for (auto& t : stl_tris) {
|
||||
int i = static_cast<int>(verts.size());
|
||||
verts.insert(verts.end(), {t.v0, t.v1, t.v2});
|
||||
idx.push_back({i, i+1, i+2});
|
||||
}
|
||||
m->build_from_triangles(verts, idx);
|
||||
} else if (str_ends_with(p, ".ply")) {
|
||||
*m = foundation::read_ply(path);
|
||||
}
|
||||
return reinterpret_cast<VdeMeshHandle>(m);
|
||||
}
|
||||
|
||||
int vde_save_mesh(VdeHandle, VdeMeshHandle mh, const char* path, const char* fmt) {
|
||||
auto* m = reinterpret_cast<HalfedgeMesh*>(mh);
|
||||
std::string f(fmt ? fmt : "obj");
|
||||
foundation::ObjMeshData data;
|
||||
for (size_t i=0;i<m->num_vertices();++i) data.vertices.push_back(m->vertex(i));
|
||||
for (size_t i=0;i<m->num_faces();++i) data.faces.push_back(m->face_vertices(static_cast<int>(i)));
|
||||
foundation::write_obj(path, data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t vde_mesh_num_vertices(VdeMeshHandle mh) { return reinterpret_cast<HalfedgeMesh*>(mh)->num_vertices(); }
|
||||
size_t vde_mesh_num_faces(VdeMeshHandle mh) { return reinterpret_cast<HalfedgeMesh*>(mh)->num_faces(); }
|
||||
|
||||
void vde_mesh_get_vertex(VdeMeshHandle mh, size_t idx, double* x, double* y, double* z) {
|
||||
auto& v = reinterpret_cast<HalfedgeMesh*>(mh)->vertex(idx);
|
||||
*x=v.x(); *y=v.y(); *z=v.z();
|
||||
}
|
||||
|
||||
void vde_mesh_get_bounds(VdeMeshHandle mh, double* mx, double* my, double* mz,
|
||||
double* Mx, double* My, double* Mz) {
|
||||
auto b = reinterpret_cast<HalfedgeMesh*>(mh)->bounds();
|
||||
*mx=b.min().x(); *my=b.min().y(); *mz=b.min().z();
|
||||
*Mx=b.max().x(); *My=b.max().y(); *Mz=b.max().z();
|
||||
}
|
||||
|
||||
void vde_mesh_free(VdeMeshHandle mh) { delete reinterpret_cast<HalfedgeMesh*>(mh); }
|
||||
|
||||
VdeMeshHandle vde_mesh_simplify(VdeHandle, VdeMeshHandle mh, double r) {
|
||||
auto* m = reinterpret_cast<HalfedgeMesh*>(mh);
|
||||
auto result = mesh::simplify_mesh(*m, {r});
|
||||
return reinterpret_cast<VdeMeshHandle>(new HalfedgeMesh(std::move(result)));
|
||||
}
|
||||
|
||||
VdeMeshHandle vde_mesh_smooth(VdeHandle, VdeMeshHandle mh, int iter) {
|
||||
auto* m = reinterpret_cast<HalfedgeMesh*>(mh);
|
||||
auto result = mesh::smooth_mesh(*m, {iter});
|
||||
return reinterpret_cast<VdeMeshHandle>(new HalfedgeMesh(std::move(result)));
|
||||
}
|
||||
|
||||
VdeMeshHandle vde_mesh_boolean(VdeHandle, VdeMeshHandle ah, VdeMeshHandle bh, int op) {
|
||||
auto* a = reinterpret_cast<HalfedgeMesh*>(ah);
|
||||
auto* b = reinterpret_cast<HalfedgeMesh*>(bh);
|
||||
auto result = mesh::mesh_boolean(*a, *b, static_cast<BooleanOp>(op));
|
||||
return reinterpret_cast<VdeMeshHandle>(new HalfedgeMesh(std::move(result)));
|
||||
}
|
||||
|
||||
// ── 曲线 ──
|
||||
VdeCurveHandle vde_curve_create_bezier(VdeHandle, const double* cp, int n, int /*dim*/) {
|
||||
std::vector<Point3D> pts;
|
||||
for (int i=0; i<n; ++i) pts.emplace_back(cp[i*3], cp[i*3+1], cp[i*3+2]);
|
||||
return reinterpret_cast<VdeCurveHandle>(new BezierCurve(std::move(pts)));
|
||||
}
|
||||
|
||||
void vde_curve_evaluate(VdeCurveHandle ch, double t, double* x, double* y, double* z) {
|
||||
auto p = reinterpret_cast<BezierCurve*>(ch)->evaluate(t);
|
||||
*x=p.x(); *y=p.y(); *z=p.z();
|
||||
}
|
||||
|
||||
int vde_curve_degree(VdeCurveHandle ch) { return reinterpret_cast<BezierCurve*>(ch)->degree(); }
|
||||
void vde_curve_free(VdeCurveHandle ch) { delete reinterpret_cast<BezierCurve*>(ch); }
|
||||
|
||||
// ── 碰撞 ──
|
||||
int vde_collision_gjk_spheres(VdeHandle, double cx1,double cy1,double cz1,double r1,
|
||||
double cx2,double cy2,double cz2,double r2) {
|
||||
auto sphere = [](Point3D c, double r) -> collision::SupportFunc {
|
||||
return [=](const Vector3D& dir) -> Point3D {
|
||||
double n = dir.norm();
|
||||
if (n<1e-12) return Point3D(c.x()+r, c.y(), c.z());
|
||||
Vector3D nd = dir / n;
|
||||
return Point3D(c.x()+nd.x()*r, c.y()+nd.y()*r, c.z()+nd.z()*r);
|
||||
};
|
||||
};
|
||||
return collision::gjk_intersect(sphere({cx1,cy1,cz1},r1), sphere({cx2,cy2,cz2},r2)) ? 1 : 0;
|
||||
}
|
||||
|
||||
int vde_collision_ray_mesh(VdeHandle, VdeMeshHandle mh, double ox,double oy,double oz,
|
||||
double dx,double dy,double dz, double* t, double* x, double* y, double* z) {
|
||||
auto* m = reinterpret_cast<HalfedgeMesh*>(mh);
|
||||
std::vector<Triangle3D> tris;
|
||||
for (size_t fi=0; fi<m->num_faces(); ++fi){
|
||||
auto vis=m->face_vertices(static_cast<int>(fi));
|
||||
if(vis.size()==3) tris.emplace_back(m->vertex(vis[0]),m->vertex(vis[1]),m->vertex(vis[2]));
|
||||
}
|
||||
spatial::BVH bvh; bvh.build(tris);
|
||||
auto hit = bvh.query_ray_nearest(Ray3Dd({ox,oy,oz},{dx,dy,dz}));
|
||||
if (hit) { *t=hit->t; *x=hit->point.x(); *y=hit->point.y(); *z=hit->point.z(); return 1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ── 约束求解 ──
|
||||
VdeSolverHandle vde_solver_create(void) { return reinterpret_cast<VdeSolverHandle>(new sketch::ConstraintSolver()); }
|
||||
int vde_solver_add_point(VdeSolverHandle sh, double x, double y, int fixed) {
|
||||
return reinterpret_cast<sketch::ConstraintSolver*>(sh)->add_point(x,y,fixed!=0);
|
||||
}
|
||||
int vde_solver_add_line(VdeSolverHandle sh, int p0, int p1) {
|
||||
return reinterpret_cast<sketch::ConstraintSolver*>(sh)->add_line(p0,p1);
|
||||
}
|
||||
void vde_solver_add_distance_constraint(VdeSolverHandle sh, int p0, int p1, double d) {
|
||||
reinterpret_cast<sketch::ConstraintSolver*>(sh)->add_constraint(sketch::ConstraintType::Distance,{p0,p1},d);
|
||||
}
|
||||
void vde_solver_add_horizontal(VdeSolverHandle sh, int lid) {
|
||||
reinterpret_cast<sketch::ConstraintSolver*>(sh)->add_constraint(sketch::ConstraintType::Horizontal,{lid});
|
||||
}
|
||||
void vde_solver_add_vertical(VdeSolverHandle sh, int lid) {
|
||||
reinterpret_cast<sketch::ConstraintSolver*>(sh)->add_constraint(sketch::ConstraintType::Vertical,{lid});
|
||||
}
|
||||
int vde_solver_solve(VdeSolverHandle sh, int max_iter, double tol) {
|
||||
return reinterpret_cast<sketch::ConstraintSolver*>(sh)->solve(max_iter,tol).converged ? 1 : 0;
|
||||
}
|
||||
void vde_solver_get_point(VdeSolverHandle sh, int idx, double* x, double* y) {
|
||||
auto& pts = reinterpret_cast<sketch::ConstraintSolver*>(sh)->points();
|
||||
if (idx>=0 && idx<(int)pts.size()) { *x=pts[idx].pos.x(); *y=pts[idx].pos.y(); }
|
||||
}
|
||||
void vde_solver_free(VdeSolverHandle sh) { delete reinterpret_cast<sketch::ConstraintSolver*>(sh); }
|
||||
|
||||
// ── B-Rep ──
|
||||
VdeBodyHandle vde_body_create_box(VdeHandle, double w,double h,double d) {
|
||||
return reinterpret_cast<VdeBodyHandle>(new brep::BrepModel(brep::make_box(w,h,d)));
|
||||
}
|
||||
VdeBodyHandle vde_body_create_cylinder(VdeHandle, double r,double h,int segs) {
|
||||
return reinterpret_cast<VdeBodyHandle>(new brep::BrepModel(brep::make_cylinder(r,h,segs)));
|
||||
}
|
||||
VdeBodyHandle vde_body_create_sphere(VdeHandle, double r,int su,int sv) {
|
||||
return reinterpret_cast<VdeBodyHandle>(new brep::BrepModel(brep::make_sphere(r,su,sv)));
|
||||
}
|
||||
VdeMeshHandle vde_body_to_mesh(VdeHandle, VdeBodyHandle bh, double defl) {
|
||||
auto* body = reinterpret_cast<brep::BrepModel*>(bh);
|
||||
return reinterpret_cast<VdeMeshHandle>(new HalfedgeMesh(body->to_mesh(defl)));
|
||||
}
|
||||
void vde_body_free(VdeBodyHandle bh) { delete reinterpret_cast<brep::BrepModel*>(bh); }
|
||||
|
||||
// ── 序列化 ──
|
||||
size_t vde_serialize_mesh(VdeHandle, VdeMeshHandle mh, uint8_t** out) {
|
||||
auto* m = reinterpret_cast<HalfedgeMesh*>(mh);
|
||||
auto data = foundation::BinarySerializer::serialize(*m);
|
||||
*out = new uint8_t[data.size()];
|
||||
std::memcpy(*out, data.data(), data.size());
|
||||
return data.size();
|
||||
}
|
||||
VdeMeshHandle vde_deserialize_mesh(VdeHandle, const uint8_t* data, size_t len) {
|
||||
std::vector<uint8_t> buf(data, data+len);
|
||||
auto m = foundation::BinarySerializer::deserialize(buf);
|
||||
return reinterpret_cast<VdeMeshHandle>(new HalfedgeMesh(std::move(m)));
|
||||
}
|
||||
void vde_free_buffer(uint8_t* p) { delete[] p; }
|
||||
|
||||
} // extern "C"
|
||||
Reference in New Issue
Block a user