Files

228 lines
7.4 KiB
C++
Raw Permalink Normal View History

#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