feat(v1.0.1): P2 improvements — TolerantEdge + .vde format + boolean fallback
P2 — TolerantEdge (ACIS): - TolerantEdge: tube_radius, path_tolerance, is_within_tube, intersects - detect_tolerance_conflicts: VertexInSphere/EdgeInTube/FaceGap - resolve_tolerance_conflicts: auto merge vertices/edges/gaps P2 — .vde Native Format (ACIS): - Binary format: VdeHeader(32B), 6 SerializationSections - save_vde/load_vde: full B-Rep topology + geometry + tolerance + attributes - save_vde_json: debuggable JSON export P2 — Boolean Fallback Ladder (Parasolid): - 4-level cascade: SSI → tolerant → mesh → SDF - Level 4 never fails (SDF + MC mathematical guarantee) - BooleanFallbackResult with per-level diagnostics Total: +~2650 lines across 8 files
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file boolean_fallback.h
|
||||
* @brief 4级布尔策略回退阶梯
|
||||
*
|
||||
* 当精确SSI布尔失败时,自动降级尝试更鲁棒的布尔策略:
|
||||
*
|
||||
* Level 1: ssi_boolean — 精确SSI + 全局容差(B-Rep)
|
||||
* Level 2: tolerant_boolean — heal → boolean → heal,容差放大×10(B-Rep)
|
||||
* Level 3: mesh_boolean — 网格布尔回退(三角网格)
|
||||
* Level 4: SDF boolean — SDF → min/max → Marching Cubes(永不失败)
|
||||
*
|
||||
* 每一级失败后自动尝试下一级,记录完整的诊断信息。
|
||||
*
|
||||
* @ingroup brep
|
||||
*/
|
||||
#include "vde/brep/brep.h"
|
||||
#include "vde/brep/tolerant_modeling.h"
|
||||
#include "vde/mesh/halfedge_mesh.h"
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace vde::brep {
|
||||
|
||||
/// 布尔操作类型(与 vde::mesh::BooleanOp 对齐)
|
||||
enum class FallbackBoolOp {
|
||||
Union = 0, ///< A ∪ B
|
||||
Intersection = 1, ///< A ∩ B
|
||||
Difference = 2 ///< A \ B
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// BooleanFallbackResult — 回退结果
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief 布尔策略回退结果
|
||||
*
|
||||
* 包含最终使用的级别、结果几何体和完整的诊断链。
|
||||
* final_level = 1~4,数值越小质量越高。
|
||||
*/
|
||||
struct BooleanFallbackResult {
|
||||
int final_level = 4; ///< 最终使用的级别 (1-4)
|
||||
mesh::HalfedgeMesh result_body; ///< 结果几何体(统一为网格表示)
|
||||
std::optional<BrepModel> brep_body; ///< B-Rep结果(仅Level 1/2有效)
|
||||
BooleanDiagnostic diagnostic; ///< 最终诊断(融合各级信息)
|
||||
std::vector<int> levels_tried; ///< 尝试过的级别(含失败的)
|
||||
std::vector<BooleanDiagnostic> level_diagnostics; ///< 每一级的诊断快照
|
||||
bool brep_quality = false; ///< 结果是否为B-Rep精度
|
||||
|
||||
/// 结果是否成功(至少有一级成功)
|
||||
[[nodiscard]] bool success() const {
|
||||
return result_body.num_vertices() > 0 || !diagnostic.error_message.empty();
|
||||
}
|
||||
|
||||
/// 摘要报告
|
||||
[[nodiscard]] std::string summary() const;
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// brep_boolean_robust — 四级策略回退
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief 鲁棒布尔运算(四级策略回退)
|
||||
*
|
||||
* 按优先级依次尝试4种布尔策略,任一成功即返回。
|
||||
*
|
||||
* **回退阶梯:**
|
||||
*
|
||||
* | 级别 | 策略 | 失败条件 | 输出类型 |
|
||||
* |------|------------------|--------------------------|---------|
|
||||
* | 1 | ssi_boolean | 有 failed_faces | B-Rep |
|
||||
* | 2 | tolerant_boolean | 仍有 failed_faces | B-Rep |
|
||||
* | 3 | mesh_boolean | 崩溃/异常 | 网格 |
|
||||
* | 4 | SDF boolean | 永不失败(数学保证) | 网格 |
|
||||
*
|
||||
* Level 2 在 Level 1 失败后使用放大10倍的容差重新尝试。
|
||||
* Level 3 将两实体 tessellate 为三角网格后执行网格布尔。
|
||||
* Level 4 使用 SDF + Marching Cubes 保证总能返回结果。
|
||||
*
|
||||
* @param a 第一个 B-Rep 实体
|
||||
* @param b 第二个 B-Rep 实体
|
||||
* @param op 布尔操作类型
|
||||
* @return BooleanFallbackResult 含结果和诊断链
|
||||
*
|
||||
* @code{.cpp}
|
||||
* auto result = brep_boolean_robust(box, sphere, FallbackBoolOp::Union);
|
||||
* if (result.final_level == 1) {
|
||||
* // 精确 B-Rep 结果
|
||||
* export_step(result.brep_body.value(), "out.step");
|
||||
* } else {
|
||||
* // 使用网格结果
|
||||
* export_obj(result.result_body, "out.obj");
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
[[nodiscard]] BooleanFallbackResult brep_boolean_robust(
|
||||
const BrepModel& a,
|
||||
const BrepModel& b,
|
||||
FallbackBoolOp op);
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// 每级内部函数(跨编译单元可见,便于测试)
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief Level 1: SSI精确布尔
|
||||
* @return pair<BooleanDiagnostic, bool> — 第二个为是否成功
|
||||
*/
|
||||
[[nodiscard]] std::pair<BooleanDiagnostic, bool>
|
||||
try_ssi_boolean(const BrepModel& a, const BrepModel& b, FallbackBoolOp op);
|
||||
|
||||
/**
|
||||
* @brief Level 2: 容差感知布尔(容差放大×10)
|
||||
* @return pair<BooleanDiagnostic, bool>
|
||||
*/
|
||||
[[nodiscard]] std::pair<BooleanDiagnostic, bool>
|
||||
try_tolerant_boolean(const BrepModel& a, const BrepModel& b, FallbackBoolOp op);
|
||||
|
||||
/**
|
||||
* @brief Level 3: 网格布尔回退
|
||||
* @return pair<mesh::HalfedgeMesh, bool> — 第二个为是否成功
|
||||
*/
|
||||
[[nodiscard]] std::pair<mesh::HalfedgeMesh, bool>
|
||||
try_mesh_boolean_fallback(const BrepModel& a, const BrepModel& b, FallbackBoolOp op);
|
||||
|
||||
/**
|
||||
* @brief Level 4: SDF布尔(永不失败)
|
||||
* @return mesh::HalfedgeMesh 结果网格
|
||||
*/
|
||||
[[nodiscard]] mesh::HalfedgeMesh
|
||||
try_sdf_boolean_fallback(const BrepModel& a, const BrepModel& b, FallbackBoolOp op);
|
||||
|
||||
} // namespace vde::brep
|
||||
@@ -313,6 +313,21 @@ public:
|
||||
/** @brief 获取所有环的只读引用 */
|
||||
[[nodiscard]] const std::vector<TopoLoop>& all_loops() const { return loops_; }
|
||||
|
||||
/** @brief 获取所有壳的只读引用 */
|
||||
[[nodiscard]] const std::vector<TopoShell>& all_shells() const { return shells_; }
|
||||
|
||||
/** @brief 获取所有体的只读引用 */
|
||||
[[nodiscard]] const std::vector<TopoBody>& all_bodies() const { return bodies_; }
|
||||
|
||||
/** @brief 获取所有顶点的只读引用 */
|
||||
[[nodiscard]] const std::vector<TopoVertex>& all_vertices() const { return vertices_; }
|
||||
|
||||
/** @brief 获取所有边的只读引用 */
|
||||
[[nodiscard]] const std::vector<TopoEdge>& all_edges() const { return edges_; }
|
||||
|
||||
/** @brief 获取所有面的只读引用 */
|
||||
[[nodiscard]] const std::vector<TopoFace>& all_faces() const { return faces_; }
|
||||
|
||||
// ── 几何/拓扑查询 ──
|
||||
|
||||
/**
|
||||
|
||||
@@ -62,21 +62,33 @@ struct TolerantVertex {
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 带容差范围的拓扑边
|
||||
* @brief 带容差范围的拓扑边(管状边模型)
|
||||
*
|
||||
* 将边建模为以其中心线为轴、tube_radius 为半径的圆柱管。
|
||||
* 额外存储:
|
||||
* - tolerance: 边的曲线拟合容差
|
||||
* - tube_radius: 管半径(自适应=该边两端点容差的最大值)
|
||||
* - path_tolerance: 路径允许最大偏移(曲线拟合误差上限)
|
||||
* - is_degenerate: 退化边(两端点重合或长度<tolerance)
|
||||
* - gap_flag: 标记此边参与间隙桥接
|
||||
*
|
||||
* 管状边用于容差冲突检测:若两条边的管相交但边本身不共线,
|
||||
* 则判定为 EdgeInTube 冲突。
|
||||
*/
|
||||
struct TolerantEdge {
|
||||
int id; ///< 边 ID
|
||||
int id; ///< 边 ID(即 edge_id)
|
||||
int v_start, v_end; ///< 端点顶点 ID
|
||||
double tolerance = 1e-6; ///< 曲线拟合容差
|
||||
double tube_radius = 1e-6; ///< 管半径(自适应=两端点容差的最大值)
|
||||
double path_tolerance = 1e-4; ///< 路径允许最大偏移
|
||||
bool is_degenerate = false; ///< 退化边(长度 < tolerance)
|
||||
bool gap_flag = false; ///< 参与间隙桥接
|
||||
bool is_tangent_contact = false; ///< 切触边(奇异性处理)
|
||||
|
||||
// ── 缓存的端点坐标(用于 is_within_tube / intersects 的无 body 调用) ──
|
||||
Point3D p_start; ///< 起点坐标(缓存)
|
||||
Point3D p_end; ///< 终点坐标(缓存)
|
||||
|
||||
/// 从普通 TopoEdge 构造
|
||||
static TolerantEdge from_topo(const TopoEdge& te) {
|
||||
TolerantEdge res;
|
||||
@@ -92,6 +104,99 @@ struct TolerantEdge {
|
||||
const auto& v1 = body.vertex(v_end);
|
||||
return (v1.point - v0.point).norm() < tolerance;
|
||||
}
|
||||
|
||||
/// 从 BrepModel 计算并缓存端点坐标 + 自适应管半径
|
||||
void cache_from_body(const BrepModel& body) {
|
||||
p_start = body.vertex(v_start).point;
|
||||
p_end = body.vertex(v_end).point;
|
||||
tube_radius = std::max(body.vertex(v_start).tolerance,
|
||||
body.vertex(v_end).tolerance);
|
||||
if (tube_radius < tolerance) tube_radius = tolerance;
|
||||
}
|
||||
|
||||
/// 检测点是否在管状边内部(点到线段距离 < tube_radius)
|
||||
[[nodiscard]] bool is_within_tube(const Point3D& point) const {
|
||||
Vector3D ab = p_end - p_start;
|
||||
double len_sq = ab.squaredNorm();
|
||||
if (len_sq < 1e-20) {
|
||||
// 退化边:退化为球体
|
||||
return (point - p_start).norm() < tube_radius;
|
||||
}
|
||||
Vector3D ap = point - p_start;
|
||||
double t = ap.dot(ab) / len_sq;
|
||||
t = std::max(0.0, std::min(1.0, t));
|
||||
Point3D closest = p_start + ab * t;
|
||||
return (point - closest).norm() < tube_radius;
|
||||
}
|
||||
|
||||
/// 检测两条边管是否相交(线段间最小距离 < tube_radius_a + tube_radius_b)
|
||||
[[nodiscard]] bool intersects(const TolerantEdge& other) const {
|
||||
double min_dist = segment_segment_distance(
|
||||
p_start, p_end, other.p_start, other.p_end);
|
||||
return min_dist < (tube_radius + other.tube_radius);
|
||||
}
|
||||
|
||||
/// 两条线段之间的最小距离
|
||||
[[nodiscard]] static double segment_segment_distance(
|
||||
const Point3D& a0, const Point3D& a1,
|
||||
const Point3D& b0, const Point3D& b1)
|
||||
{
|
||||
Vector3D d1 = a1 - a0;
|
||||
Vector3D d2 = b1 - b0;
|
||||
Vector3D r = a0 - b0;
|
||||
|
||||
double a = d1.squaredNorm();
|
||||
double e = d2.squaredNorm();
|
||||
double f = d2.dot(r);
|
||||
|
||||
// 处理退化线段
|
||||
double s, t;
|
||||
|
||||
if (a < 1e-20 && e < 1e-20) {
|
||||
// 两条线段都退化为点
|
||||
return r.norm();
|
||||
}
|
||||
if (a < 1e-20) {
|
||||
// 第一条线段退化为点
|
||||
s = 0.0;
|
||||
t = std::max(0.0, std::min(1.0, f / e));
|
||||
} else {
|
||||
double c = d1.dot(r);
|
||||
if (e < 1e-20) {
|
||||
// 第二条线段退化为点
|
||||
t = 0.0;
|
||||
s = std::max(0.0, std::min(1.0, -c / a));
|
||||
} else {
|
||||
double b = d1.dot(d2);
|
||||
double denom = a * e - b * b;
|
||||
// 并行线段特殊处理
|
||||
if (std::abs(denom) < 1e-20) {
|
||||
s = 0.0;
|
||||
} else {
|
||||
s = std::max(0.0, std::min(1.0, (b * f - c * e) / denom));
|
||||
}
|
||||
double tnom = b * s + f;
|
||||
t = std::max(0.0, std::min(1.0, tnom / e));
|
||||
}
|
||||
}
|
||||
|
||||
Point3D closest_a = a0 + d1 * s;
|
||||
Point3D closest_b = b0 + d2 * t;
|
||||
return (closest_a - closest_b).norm();
|
||||
}
|
||||
|
||||
/// 获取边的单位方向向量
|
||||
[[nodiscard]] Vector3D direction() const {
|
||||
Vector3D d = p_end - p_start;
|
||||
double n = d.norm();
|
||||
if (n < 1e-12) return Vector3D(1, 0, 0);
|
||||
return d / n;
|
||||
}
|
||||
|
||||
/// 获取边的长度
|
||||
[[nodiscard]] double length() const {
|
||||
return (p_end - p_start).norm();
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
@@ -153,6 +258,81 @@ struct BooleanDiagnostic {
|
||||
[[nodiscard]] std::string report() const;
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// ToleranceConflict — 容差冲突自动检测
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/// 容差冲突类型
|
||||
enum class ConflictType {
|
||||
VertexInSphere, ///< 顶点球体重叠但未合并
|
||||
EdgeInTube, ///< 边管相交但未共线
|
||||
FaceGap ///< 面间间隙 < 容差
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 容差冲突记录
|
||||
*
|
||||
* 记录检测到的容差冲突类型、涉及元素和推荐修复策略。
|
||||
*/
|
||||
struct ToleranceConflict {
|
||||
ConflictType type; ///< 冲突类型
|
||||
int element_id_a = -1; ///< 冲突元素 A 的 ID
|
||||
int element_id_b = -1; ///< 冲突元素 B 的 ID
|
||||
double gap_size = 0.0; ///< 冲突的几何尺寸(距离/间隙值)
|
||||
std::string resolution; ///< 建议的合并/修复策略
|
||||
|
||||
/// 是否为顶点球体冲突
|
||||
[[nodiscard]] bool is_vertex_conflict() const {
|
||||
return type == ConflictType::VertexInSphere;
|
||||
}
|
||||
|
||||
/// 是否为边管冲突
|
||||
[[nodiscard]] bool is_edge_conflict() const {
|
||||
return type == ConflictType::EdgeInTube;
|
||||
}
|
||||
|
||||
/// 是否为面间隙冲突
|
||||
[[nodiscard]] bool is_face_conflict() const {
|
||||
return type == ConflictType::FaceGap;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 检测 B-Rep 模型中的容差冲突
|
||||
*
|
||||
* 执行三项检测:
|
||||
* 1. 顶点球体重叠:两点距离 < tolerance_a + tolerance_b 但尚未合并
|
||||
* 2. 边管相交:两条边的管状区域相交但边不共线
|
||||
* 3. 面间间隙:面之间存在微小间隙(自由边距离 < 容差)
|
||||
*
|
||||
* @param body 待检测的 B-Rep 模型
|
||||
* @param vertex_tolerance 顶点合并容差,默认 1e-6
|
||||
* @param edge_tolerance 边管容差倍率,默认 2.0(顶点容差的倍数)
|
||||
* @param face_gap_tolerance 面间隙容差,默认 1e-4
|
||||
* @return 检测到的冲突列表
|
||||
*/
|
||||
[[nodiscard]] std::vector<ToleranceConflict> detect_tolerance_conflicts(
|
||||
const BrepModel& body,
|
||||
double vertex_tolerance = 1e-6,
|
||||
double edge_tolerance = 2.0,
|
||||
double face_gap_tolerance = 1e-4);
|
||||
|
||||
/**
|
||||
* @brief 解析并修复容差冲突
|
||||
*
|
||||
* 按冲突类型分类处理:
|
||||
* - VertexInSphere → 调用 heal_gaps 合并顶点
|
||||
* - EdgeInTube → 调用 heal_merge_edges 合并边
|
||||
* - FaceGap → 调用 heal_gaps 闭合间隙
|
||||
*
|
||||
* @param body 待修复的 B-Rep 模型(原地修改)
|
||||
* @param conflicts 冲突列表(来自 detect_tolerance_conflicts)
|
||||
* @return 修复的冲突数量
|
||||
*/
|
||||
int resolve_tolerance_conflicts(
|
||||
BrepModel& body,
|
||||
const std::vector<ToleranceConflict>& conflicts);
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Public API
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file vde_format.h
|
||||
* @brief VDE 原生格式(.vde)二进制读写
|
||||
*
|
||||
* VDE 原生格式是一种自包含的二进制格式,用于高效保存和加载
|
||||
* B-Rep 模型的完整数据(拓扑、几何、容差、属性、建模历史)。
|
||||
*
|
||||
* ## 文件结构
|
||||
*
|
||||
* ```
|
||||
* VdeHeader (32 bytes)
|
||||
* ├─ magic[4] = "VDE1"
|
||||
* ├─ version (u32) = 1
|
||||
* ├─ flags (u32)
|
||||
* └─ data_offset (u32)
|
||||
*
|
||||
* SerializationSection 数据块:
|
||||
* ├─ SECTION_TOPOLOGY — B-Rep 拓扑(V/E/L/F/S/B)
|
||||
* ├─ SECTION_GEOMETRY — 几何数据(顶点坐标/曲面控制点/裁剪参数/NURBS曲线)
|
||||
* ├─ SECTION_TOLERANCE — ToleranceConfig
|
||||
* ├─ SECTION_ATTRIBUTES — 面/边/顶点/体属性(颜色/名称/自定义数据)
|
||||
* └─ SECTION_HISTORY — 建模历史 FeatureNode 列表(可选)
|
||||
* ```
|
||||
*
|
||||
* ## 使用示例
|
||||
*
|
||||
* @code{.cpp}
|
||||
* #include "vde/foundation/vde_format.h"
|
||||
* using namespace vde::foundation;
|
||||
*
|
||||
* // 保存
|
||||
* brep::BrepModel body = make_box(10, 5, 3);
|
||||
* save_vde(body, "output.vde");
|
||||
*
|
||||
* // 加载
|
||||
* auto loaded = load_vde("output.vde");
|
||||
*
|
||||
* // JSON 调试
|
||||
* save_vde_json(body, "output.vde.json");
|
||||
* @endcode
|
||||
*
|
||||
* @ingroup foundation
|
||||
*/
|
||||
#include "vde/brep/brep.h"
|
||||
#include "vde/brep/tolerance.h"
|
||||
#include "vde/brep/feature_tree.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace vde::foundation {
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// VDE 头部
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
/// VDE 文件魔术字节
|
||||
constexpr uint32_t VDE_MAGIC_FOURCC = 0x31454456; // "VDE1" (little-endian)
|
||||
|
||||
/// VDE 文件当前版本号
|
||||
constexpr uint32_t VDE_FILE_VERSION = 1;
|
||||
|
||||
/// VDE 标志位:是否包含建模历史
|
||||
constexpr uint32_t VDE_FLAG_HAS_HISTORY = 0x00000001;
|
||||
/// VDE 标志位:是否包含属性数据
|
||||
constexpr uint32_t VDE_FLAG_HAS_ATTRIBUTES = 0x00000002;
|
||||
/// VDE 标志位:是否包含裁剪曲面
|
||||
constexpr uint32_t VDE_FLAG_HAS_TRIMMED = 0x00000004;
|
||||
|
||||
/**
|
||||
* @brief VDE 文件头(32 字节)
|
||||
*
|
||||
* 二进制布局(小端序):
|
||||
* - 字节 0–3: magic "VDE1" (uint32, 0x31454456)
|
||||
* - 字节 4–7: version (uint32)
|
||||
* - 字节 8–11: flags (uint32)
|
||||
* - 字节 12–15: data_offset (uint32, 数据区起始偏移)
|
||||
* - 字节 16–31: reserved (16 bytes, 当前填充 0)
|
||||
*/
|
||||
struct VdeHeader {
|
||||
uint32_t magic; ///< 魔术数字 0x31454456 ("VDE1")
|
||||
uint32_t version; ///< 格式版本号
|
||||
uint32_t flags; ///< 标志位(VDE_FLAG_* 组合)
|
||||
uint32_t data_offset; ///< 数据区相对于文件头的偏移(字节)
|
||||
uint8_t reserved[16];///< 保留字段
|
||||
|
||||
/// 构造默认头部(版本=1,标志=0,偏移=32)
|
||||
VdeHeader();
|
||||
|
||||
/// 从内存解析头部
|
||||
static VdeHeader parse(const uint8_t* data, size_t size);
|
||||
|
||||
/// 将头部编码写入缓冲区
|
||||
void encode(std::vector<uint8_t>& buf) const;
|
||||
|
||||
/// 验证头部有效性
|
||||
[[nodiscard]] bool is_valid() const;
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// 序列化段枚举
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief 序列化段类型标识
|
||||
*
|
||||
* 每个段由 [section_id:uint16][section_size:uint64][data...] 组成。
|
||||
* 段按顺序写入,读取时按需跳过未知段。
|
||||
*/
|
||||
enum class SerializationSection : uint16_t {
|
||||
SECTION_HEADER = 0, ///< 文件头(不在数据区重复)
|
||||
SECTION_TOPOLOGY = 1, ///< B-Rep 拓扑(V/E/L/F/S/B)
|
||||
SECTION_GEOMETRY = 2, ///< 几何数据(曲面/曲线/裁剪参数)
|
||||
SECTION_TOLERANCE = 3, ///< ToleranceConfig 容差配置
|
||||
SECTION_ATTRIBUTES = 4, ///< 面/边/顶点/体属性
|
||||
SECTION_HISTORY = 5, ///< 建模历史 FeatureNode 列表
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// 属性数据结构
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
/// 顶点属性
|
||||
struct VdeVertexAttribute {
|
||||
int vertex_id = -1;
|
||||
std::string name;
|
||||
uint32_t color_rgba = 0xFFFFFFFF; ///< RGBA 颜色
|
||||
std::string custom_data; ///< 自定义数据(键值对或 JSON)
|
||||
};
|
||||
|
||||
/// 边属性
|
||||
struct VdeEdgeAttribute {
|
||||
int edge_id = -1;
|
||||
std::string name;
|
||||
uint32_t color_rgba = 0xFFFFFFFF;
|
||||
std::string custom_data;
|
||||
};
|
||||
|
||||
/// 面属性
|
||||
struct VdeFaceAttribute {
|
||||
int face_id = -1;
|
||||
std::string name;
|
||||
uint32_t color_rgba = 0xFFFFFFFF;
|
||||
std::string custom_data;
|
||||
};
|
||||
|
||||
/// 体属性
|
||||
struct VdeBodyAttribute {
|
||||
int body_id = -1;
|
||||
std::string name;
|
||||
std::string custom_data;
|
||||
};
|
||||
|
||||
/// 属性集合
|
||||
struct VdeAttributes {
|
||||
std::vector<VdeVertexAttribute> vertex_attrs;
|
||||
std::vector<VdeEdgeAttribute> edge_attrs;
|
||||
std::vector<VdeFaceAttribute> face_attrs;
|
||||
std::vector<VdeBodyAttribute> body_attrs;
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// VDE 格式主 API
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief 将 B-Rep 模型保存为 VDE 二进制文件
|
||||
*
|
||||
* 写入完整的模型数据,包括拓扑、几何、容差、属性和建模历史。
|
||||
*
|
||||
* @param body B-Rep 模型
|
||||
* @param path 输出文件路径(.vde)
|
||||
* @param attrs 面/边/顶点/体属性(可选,默认空)
|
||||
* @param history 建模历史节点列表(可选,默认空)
|
||||
* @return true 保存成功
|
||||
*
|
||||
* @throws std::runtime_error 写入失败
|
||||
*/
|
||||
bool save_vde(const brep::BrepModel& body,
|
||||
const std::string& path,
|
||||
const VdeAttributes& attrs = {},
|
||||
const std::vector<brep::FeatureNode>* history = nullptr);
|
||||
|
||||
/**
|
||||
* @brief 从 VDE 二进制文件加载 B-Rep 模型
|
||||
*
|
||||
* 读取并解析 .vde 格式文件,恢复完整的 B-Rep 模型数据。
|
||||
*
|
||||
* @param path 输入文件路径(.vde)
|
||||
* @return 加载的 BrepModel
|
||||
*
|
||||
* @throws std::runtime_error 文件无效或解析失败
|
||||
*/
|
||||
[[nodiscard]] brep::BrepModel load_vde(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 从 VDE 二进制文件加载 B-Rep 模型及属性
|
||||
*
|
||||
* @param path 输入文件路径(.vde)
|
||||
* @param out_attrs 输出属性数据
|
||||
* @return 加载的 BrepModel
|
||||
*/
|
||||
[[nodiscard]] brep::BrepModel load_vde_with_attrs(const std::string& path,
|
||||
VdeAttributes& out_attrs);
|
||||
|
||||
/**
|
||||
* @brief 将 B-Rep 模型保存为 VDE JSON 文件(可调试格式)
|
||||
*
|
||||
* 以人类可读的 JSON 格式导出模型数据,方便调试和检查。
|
||||
* JSON 版本不包含二进制段,而是以键值对形式组织。
|
||||
*
|
||||
* @param body B-Rep 模型
|
||||
* @param path 输出文件路径(.vde.json 或 .json)
|
||||
* @param attrs 属性数据(可选)
|
||||
* @return true 保存成功
|
||||
*/
|
||||
bool save_vde_json(const brep::BrepModel& body,
|
||||
const std::string& path,
|
||||
const VdeAttributes& attrs = {});
|
||||
|
||||
/**
|
||||
* @brief 将 B-Rep 模型序列化为 VDE 二进制缓冲区
|
||||
*
|
||||
* @param body B-Rep 模型
|
||||
* @param attrs 属性数据(可选)
|
||||
* @param history 建模历史节点(可选)
|
||||
* @return 二进制数据
|
||||
*/
|
||||
[[nodiscard]] std::vector<uint8_t> serialize_vde(
|
||||
const brep::BrepModel& body,
|
||||
const VdeAttributes& attrs = {},
|
||||
const std::vector<brep::FeatureNode>* history = nullptr);
|
||||
|
||||
/**
|
||||
* @brief 从二进制缓冲区反序列化 VDE 模型
|
||||
*
|
||||
* @param data 二进制数据
|
||||
* @return 加载的 BrepModel
|
||||
*/
|
||||
[[nodiscard]] brep::BrepModel deserialize_vde(const std::vector<uint8_t>& data);
|
||||
|
||||
} // namespace vde::foundation
|
||||
Reference in New Issue
Block a user