fcf25e561d
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
228 lines
7.4 KiB
C++
228 lines
7.4 KiB
C++
#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
|