feat(v4.4): complete remaining v4.1-v4.4 features + precision tolerance + Euler ops
v4.1 收尾: - IncrementalUpdateEngine: dirty flag propagation, cache invalidation - LargeAssembly: InstanceCache, assembly instancing - STEP import: robust/graceful parsing with skip tracking v4.3 分析工具: - Mass properties (volume, centroid, inertia tensor) - Clearance analysis, wall thickness analysis - Enhanced drawing: hidden-line removal, offset sections, BOM - DXF import (LINE/CIRCLE/ARC/LWPOLYLINE/SPLINE → B-Rep extrusion) v4.4 地基加固: - ToleranceChain: RSS cumulative tolerance propagation (7 tests) - Euler operations: MEV/KEV/MEF/KEF/KEMR/MEKR (20 tests) - Replace hardcoded tolerances with ToleranceConfig in validate - Fix incremental_update test API mismatch (15/15 pass on Linux) Docs: - v4.1-v4.4 development plans + roadmap updated - v4.4 marked complete on Linux 30 files, +3424/-210
This commit is contained in:
@@ -50,6 +50,7 @@
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace vde::brep {
|
||||
using core::Point3D;
|
||||
@@ -349,11 +350,12 @@ public:
|
||||
*/
|
||||
[[nodiscard]] std::vector<int> vertex_edges(int vertex_id) const;
|
||||
|
||||
// ── 友元:拓扑修复需要直接访问内部数据 ──
|
||||
// ── 友元:拓扑修复 / 欧拉操作需要直接访问内部数据 ──
|
||||
friend int heal_merge_vertices(BrepModel&, double);
|
||||
friend int heal_merge_edges(BrepModel&, double);
|
||||
friend int heal_close_gaps(BrepModel&, double);
|
||||
friend int heal_orientation(BrepModel&);
|
||||
friend class EulerOp;
|
||||
|
||||
private:
|
||||
std::vector<TopoVertex> vertices_;
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file dxf_import.h
|
||||
* @brief DXF 文件导入(AutoCAD R12+ 兼容)
|
||||
*
|
||||
* 解析 DXF 格式的 2D 图形,转换为 B-Rep 轮廓。
|
||||
* 支持的实体类型:LINE, CIRCLE, ARC, LWPOLYLINE, SPLINE, POLYLINE
|
||||
*
|
||||
* @ingroup brep
|
||||
*/
|
||||
#include "vde/brep/brep.h"
|
||||
#include "vde/core/point.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace vde::brep {
|
||||
|
||||
/// 2D 轮廓点(用于拉伸生成体)
|
||||
struct DxfContour {
|
||||
std::string layer; ///< 图层名称
|
||||
std::vector<core::Point3D> points; ///< 轮廓点序列(Z=0)
|
||||
bool closed = false; ///< 是否闭合
|
||||
};
|
||||
|
||||
/// DXF 导入结果
|
||||
struct DxfImportResult {
|
||||
std::vector<DxfContour> contours; ///< 提取的 2D 轮廓
|
||||
std::vector<std::string> layers; ///< 所有图层名称
|
||||
int entities_parsed = 0; ///< 成功解析的实体数
|
||||
int entities_skipped = 0; ///< 跳过的实体数
|
||||
};
|
||||
|
||||
/// 导入 DXF 文件,提取 2D 轮廓
|
||||
/// @param filepath .dxf 文件路径
|
||||
/// @return 轮廓列表 + 统计信息
|
||||
[[nodiscard]] DxfImportResult import_dxf(const std::string& filepath);
|
||||
|
||||
/// 从内存字符串导入 DXF
|
||||
/// @param dxf_data DXF 文件内容
|
||||
/// @return 轮廓列表 + 统计信息
|
||||
[[nodiscard]] DxfImportResult import_dxf_from_string(const std::string& dxf_data);
|
||||
|
||||
/// 将 DXF 轮廓拉伸为 B-Rep 实体
|
||||
/// @param contour 2D 轮廓
|
||||
/// @param height 拉伸高度
|
||||
/// @return B-Rep 实体
|
||||
[[nodiscard]] BrepModel extrude_dxf_contour(const DxfContour& contour, double height);
|
||||
|
||||
/// 导入 DXF 并拉伸所有轮廓为实体
|
||||
/// @param filepath .dxf 文件路径
|
||||
/// @param height 拉伸高度(默认 10)
|
||||
/// @return B-Rep 实体列表(每个轮廓一个实体)
|
||||
[[nodiscard]] std::vector<BrepModel> import_dxf_as_solids(const std::string& filepath,
|
||||
double height = 10.0);
|
||||
|
||||
} // namespace vde::brep
|
||||
@@ -0,0 +1,227 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file euler_op.h
|
||||
* @brief 欧拉操作 — B-Rep 拓扑编辑原语
|
||||
*
|
||||
* 实现 Baumgart-Mäntylä 欧拉操作,是 B-Rep 底层拓扑编辑的标准原语集合。
|
||||
* 每个操作保持欧拉-庞加莱公式不变:
|
||||
* V - E + F - (L - F) = 2(S - H) + R
|
||||
*
|
||||
* 其中 V=顶点数, E=边数, F=面数, L=环数, S=壳数, H=贯穿孔数, R=内环数
|
||||
*
|
||||
* ## 操作清单
|
||||
*
|
||||
* | 操作 | 含义 | ΔV | ΔE | ΔF | ΔL |
|
||||
* |-------|------|----|----|----|----|
|
||||
* | MEV | 边上插入顶点 | +1 | +1 | 0 | 0 |
|
||||
* | KEV | 移除边上顶点 | -1 | -1 | 0 | 0 |
|
||||
* | MEF | 面内建边分裂面 | 0 | +1 | +1 | +1 |
|
||||
* | KEF | 删除边合并面 | 0 | -1 | -1 | -1 |
|
||||
* | KEMR | 删边建内环 | 0 | -1 | 0 | 0 |
|
||||
* | MEKR | 建边删内环 | 0 | +1 | 0 | 0 |
|
||||
*
|
||||
* 六个操作涵盖所有拓扑编辑需求。对偶操作(MEV↔KEV, MEF↔KEF, KEMR↔MEKR)
|
||||
* 互为逆操作。
|
||||
*
|
||||
* @ingroup brep
|
||||
*/
|
||||
|
||||
#include "vde/brep/brep.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
namespace vde::brep {
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Euler operation result
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief 欧拉操作结果
|
||||
*
|
||||
* 记录操作创建和删除的拓扑元素 ID。
|
||||
* 成功时 created/deleted 字段根据操作类型填充。
|
||||
* 失败时 error 字段包含错误信息。
|
||||
*/
|
||||
struct EulerOpResult {
|
||||
bool success = false;
|
||||
std::string error;
|
||||
|
||||
// Created elements (操作新建的)
|
||||
int new_vertex = -1;
|
||||
int new_edge = -1;
|
||||
int new_edge_2 = -1;
|
||||
int new_face = -1;
|
||||
int new_face_2 = -1;
|
||||
int new_loop = -1;
|
||||
int new_loop_2 = -1;
|
||||
|
||||
// Deleted elements (操作移除的,保留供查询)
|
||||
int deleted_vertex = -1;
|
||||
int deleted_edge = -1;
|
||||
int deleted_face = -1;
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// EulerOp — 欧拉操作引擎
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief 欧拉操作引擎
|
||||
*
|
||||
* 提供六个核心欧拉操作的静态方法。
|
||||
* 所有操作在 BrepModel 上就地执行,保持拓扑一致性。
|
||||
*
|
||||
* 使用模式:欧拉操作后应调用 euler_poincare() 验证公式不变。
|
||||
*/
|
||||
class EulerOp {
|
||||
public:
|
||||
// ─── 操作 1: MEV — Make Edge Vertex ───
|
||||
|
||||
/**
|
||||
* @brief 在边上插入顶点,将边分裂为两条边
|
||||
*
|
||||
* @code
|
||||
* Before: V1 ────E──── V2
|
||||
* After: V1 ─E1─ Vnew ─E2─ V2
|
||||
* @endcode
|
||||
*
|
||||
* @param body 目标 B-Rep 模型
|
||||
* @param edge_idx 要分裂的边索引
|
||||
* @param t 插入位置参数 (0 < t < 1),0=起点 1=终点
|
||||
* @return 操作结果(含 new_vertex, new_edge=E1, new_edge_2=E2)
|
||||
*/
|
||||
static EulerOpResult mev(BrepModel& body, int edge_idx, double t);
|
||||
|
||||
// ─── 操作 2: KEV — Kill Edge Vertex ───
|
||||
|
||||
/**
|
||||
* @brief 移除顶点并合并两侧边
|
||||
*
|
||||
* 逆操作:MEV。
|
||||
*
|
||||
* @code
|
||||
* Before: Va ─E1─ Vb ─E2─ Vc
|
||||
* After: Va ────Enew──── Vc
|
||||
* @endcode
|
||||
*
|
||||
* 要求 E1 和 E2 共线,Vb 的度为 2。
|
||||
*
|
||||
* @param body 目标 B-Rep 模型
|
||||
* @param vertex_idx 要移除的顶点索引
|
||||
* @return 操作结果(含 new_edge=Enew, deleted_vertex=Vb)
|
||||
*/
|
||||
static EulerOpResult kev(BrepModel& body, int vertex_idx);
|
||||
|
||||
// ─── 操作 3: MEF — Make Edge Face ───
|
||||
|
||||
/**
|
||||
* @brief 在面内建边,将面分裂为两个面
|
||||
*
|
||||
* @code
|
||||
* Before: ┌─────────────┐
|
||||
* After: ├──────┬──────┤ (新边垂直分割面)
|
||||
* @endcode
|
||||
*
|
||||
* @param body 目标 B-Rep 模型
|
||||
* @param face_idx 要分裂的面索引
|
||||
* @param va_idx 新边起点顶点索引(必须在面内)
|
||||
* @param vb_idx 新边终点顶点索引(必须在面内)
|
||||
* @return 操作结果(含 new_edge, new_face, new_face_2)
|
||||
*/
|
||||
static EulerOpResult mef(BrepModel& body, int face_idx, int va_idx, int vb_idx);
|
||||
|
||||
// ─── 操作 4: KEF — Kill Edge Face ───
|
||||
|
||||
/**
|
||||
* @brief 删除边并合并两侧面
|
||||
*
|
||||
* 逆操作:MEF。
|
||||
*
|
||||
* @code
|
||||
* Before: Fa │ E │ Fb (E 两侧是不同面)
|
||||
* After: Fa+Fb merged
|
||||
* @endcode
|
||||
*
|
||||
* 要求边的两个相邻面共面。
|
||||
*
|
||||
* @param body 目标 B-Rep 模型
|
||||
* @param edge_idx 要删除的边索引
|
||||
* @return 操作结果(含 new_face, deleted_edge, deleted_face)
|
||||
*/
|
||||
static EulerOpResult kef(BrepModel& body, int edge_idx);
|
||||
|
||||
// ─── 操作 5: KEMR — Kill Edge Make Ring ───
|
||||
|
||||
/**
|
||||
* @brief 删除内环上的边,合并内环
|
||||
*
|
||||
* @code
|
||||
* Before: ┌─────┐ 内环 ┌──┐
|
||||
* After: ┌──────┐ (内环扩大)
|
||||
* @endcode
|
||||
*
|
||||
* 要求边两侧是同一个面(即边在内环上)。
|
||||
*
|
||||
* @param body 目标 B-Rep 模型
|
||||
* @param edge_idx 要删除的边索引(必须在面的内环上)
|
||||
* @return 操作结果
|
||||
*/
|
||||
static EulerOpResult kemr(BrepModel& body, int edge_idx);
|
||||
|
||||
// ─── 操作 6: MEKR — Make Edge Kill Ring ───
|
||||
|
||||
/**
|
||||
* @brief 在内环上建边,分割内环
|
||||
*
|
||||
* 逆操作:KEMR。
|
||||
*
|
||||
* @param body 目标 B-Rep 模型
|
||||
* @param face_idx 包含内环的面
|
||||
* @param va_idx 内环上的起点顶点
|
||||
* @param vb_idx 内环上的终点顶点(同内环)
|
||||
* @return 操作结果(含 new_edge)
|
||||
*/
|
||||
static EulerOpResult mekr(BrepModel& body, int face_idx, int va_idx, int vb_idx);
|
||||
|
||||
// ─── 验证 ───
|
||||
|
||||
/**
|
||||
* @brief 计算欧拉-庞加莱特征数
|
||||
*
|
||||
* EP = V - E + F - (L - F) = 2(S - H) + R
|
||||
*
|
||||
* 对于简单封闭实体(S=1, H=0, R=0):EP = 2
|
||||
*
|
||||
* @return 特征数
|
||||
*/
|
||||
[[nodiscard]] static int euler_poincare(const BrepModel& body);
|
||||
|
||||
/**
|
||||
* @brief 验证模型满足欧拉-庞加莱公式
|
||||
*
|
||||
* 计算 EP 并与期望值比较。对简单实体期望 EP=2。
|
||||
*
|
||||
* @return true 如果公式成立
|
||||
*/
|
||||
[[nodiscard]] static bool verify_euler(const BrepModel& body);
|
||||
|
||||
/// 计算顶点度(相连边数)
|
||||
static int vertex_degree(const BrepModel& body, int vertex_idx);
|
||||
|
||||
private:
|
||||
/// 获取顶点所在面的边界环索引(-1 表示不在任何环中)
|
||||
static int find_vertex_in_loop(const BrepModel& body,
|
||||
int vertex_idx, const TopoLoop& loop);
|
||||
|
||||
/// 在环中查找顶点出现的位置
|
||||
static std::vector<int> find_vertex_positions_in_loop(
|
||||
const BrepModel& body, int vertex_idx, const TopoLoop& loop);
|
||||
|
||||
/// 重建壳和体以引用新的面映射
|
||||
static void rebuild_shells_with_new_faces(BrepModel& body,
|
||||
const std::map<int, int>& face_map);
|
||||
};
|
||||
|
||||
} // namespace vde::brep
|
||||
@@ -13,6 +13,9 @@
|
||||
#include "vde/brep/brep.h"
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
namespace vde::brep {
|
||||
|
||||
@@ -150,4 +153,66 @@ struct ToleranceConfig {
|
||||
*/
|
||||
[[nodiscard]] double model_tolerance(const BrepModel& body);
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Tolerance chain — 容差传播追踪
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief 容差传播链
|
||||
*
|
||||
* 追踪操作链中的容差累积。每个操作注入自身的容差贡献,
|
||||
* 末端可查询累积容差上界。
|
||||
*
|
||||
* 使用场景:
|
||||
* - 布尔运算链:求交 → 分割 → 缝合,累积容差逐级放大
|
||||
* - 特征链:拉伸 → 倒圆 → 抽壳,容差传播路径
|
||||
*
|
||||
* @code
|
||||
* ToleranceChain chain;
|
||||
* chain.push("intersect", 1e-6);
|
||||
* chain.push("split", 1e-6);
|
||||
* chain.push("sew", 1e-5);
|
||||
* double worst = chain.cumulative(); // 1.2e-5 (root-sum-square)
|
||||
* @endcode
|
||||
*/
|
||||
class ToleranceChain {
|
||||
public:
|
||||
/**
|
||||
* @brief 记录一个操作及其容差贡献
|
||||
* @param op_name 操作名称(用于调试/日志)
|
||||
* @param tol 该操作注入的容差
|
||||
*/
|
||||
void push(const std::string& op_name, double tol);
|
||||
|
||||
/**
|
||||
* @brief 累积容差
|
||||
*
|
||||
* 使用均方根 (RSS) 合成:sqrt(Σ tol²)
|
||||
* 比简单求和更保守,但比对数叠加更实用。
|
||||
*
|
||||
* @return 累积容差
|
||||
*/
|
||||
[[nodiscard]] double cumulative() const;
|
||||
|
||||
/**
|
||||
* @brief 最大单步容差
|
||||
* @return 链中最大的单步容差
|
||||
*/
|
||||
[[nodiscard]] double max_step() const;
|
||||
|
||||
/** @brief 链深度 */
|
||||
[[nodiscard]] size_t depth() const { return steps_.size(); }
|
||||
|
||||
/** @brief 所有步骤(只读) */
|
||||
[[nodiscard]] const std::vector<std::pair<std::string, double>>& steps() const {
|
||||
return steps_;
|
||||
}
|
||||
|
||||
/** @brief 清空链 */
|
||||
void clear() { steps_.clear(); }
|
||||
|
||||
private:
|
||||
std::vector<std::pair<std::string, double>> steps_;
|
||||
};
|
||||
|
||||
} // namespace vde::brep
|
||||
|
||||
Reference in New Issue
Block a user