5bfcbcb8e9
Remaining P3 item: - tangent_pull: maintain G1 tangency when pulling faces System infrastructure: - Unified ErrorCode (0-7xxx): ErrorInfo with Chinese messages - Version system: VDE_VERSION_MAJOR/MINOR/PATCH macros, vde_version() → VersionInfo, vde_version_string() - License management: LicenseTier (Community/Professional/Enterprise), LicenseKey (VDE-XXXX-XXXX, Base32 + HMAC-SHA256 signature), LicenseManager singleton with trial support - Convenience header: vde/vde_version.h Tests: 43 total (26 license + 17 version)
389 lines
15 KiB
C++
389 lines
15 KiB
C++
#pragma once
|
||
/**
|
||
* @file direct_modeling.h
|
||
* @brief 直接建模操作 — 面级推拉/偏移/移动/替换
|
||
*
|
||
* 直接建模(Direct Modeling / Synchronous Technology)允许用户
|
||
* 直接操纵几何面而不依赖特征历史树。该模块提供:
|
||
*
|
||
* | 操作 | 含义 |
|
||
* |--------------|-----------------------------------------|
|
||
* | tweak_face | 面沿法向微调偏移,邻面自动延伸保持水密 |
|
||
* | move_face | 面刚体移动(平移+旋转),检测不穿透邻面 |
|
||
* | replace_face | 面替换(交换曲面),边界兼容性检查 |
|
||
* | push_pull | 推拉:正值=挤出加材料,负值=切除减材料 |
|
||
* | offset_face | 面等距偏移 |
|
||
*
|
||
* 所有操作返回 DirectModelingResult,含成功/失败诊断及生成的新模型。
|
||
*
|
||
* @ingroup brep
|
||
*/
|
||
|
||
#include "vde/brep/brep.h"
|
||
#include "vde/curves/nurbs_surface.h"
|
||
#include "vde/core/transform.h"
|
||
#include <string>
|
||
#include <vector>
|
||
|
||
namespace vde::brep {
|
||
|
||
// ═══════════════════════════════════════════════════════════
|
||
// DirectModelingResult — 操作结果
|
||
// ═══════════════════════════════════════════════════════════
|
||
|
||
/**
|
||
* @brief 直接建模操作结果
|
||
*
|
||
* 包含成功/失败标志、错误/警告诊断信息及生成的新模型。
|
||
* 即使操作部分成功(如某些邻面未能延伸)也会在 warnings 中记录。
|
||
*/
|
||
struct DirectModelingResult {
|
||
/** @brief 操作是否成功(至少基本几何有效) */
|
||
bool success = false;
|
||
|
||
/** @brief 结果模型(success=true 时有效,否则为输入副本) */
|
||
BrepModel body;
|
||
|
||
/** @brief 错误消息列表(导致 success=false) */
|
||
std::vector<std::string> errors;
|
||
|
||
/** @brief 警告消息列表(不影响 success) */
|
||
std::vector<std::string> warnings;
|
||
|
||
/** @brief 受影响的面数 */
|
||
int affected_faces = 0;
|
||
|
||
/** @brief 新建的面数(推拉产生的侧面等) */
|
||
int new_faces = 0;
|
||
|
||
/** @brief 操作类型名称(供日志/UI 显示) */
|
||
std::string operation;
|
||
};
|
||
|
||
// ═══════════════════════════════════════════════════════════
|
||
// Direct Modeling Operations
|
||
// ═══════════════════════════════════════════════════════════
|
||
|
||
/**
|
||
* @brief 面微调:沿法向偏移指定距离
|
||
*
|
||
* 将面沿其法向量方向偏移 delta。邻面自动延伸/裁剪
|
||
* 以保持水密封闭体。适合小范围的面调整。
|
||
*
|
||
* @param body 输入 B-Rep 实体
|
||
* @param face_id 目标面索引(0..num_faces-1)
|
||
* @param delta 偏移距离(正=沿法向向外,负=向内)
|
||
* @return DirectModelingResult 含新模型和诊断信息
|
||
*
|
||
* @pre face_id 在 [0, body.num_faces()) 范围内
|
||
* @pre 实体应为封闭水密体
|
||
*
|
||
* @code{.cpp}
|
||
* auto box = make_box(2, 2, 2);
|
||
* auto result = tweak_face(box, 0, 0.5); // 顶面上移 0.5
|
||
* // 四个侧面自动延伸保持水密
|
||
* @endcode
|
||
*/
|
||
[[nodiscard]] DirectModelingResult tweak_face(const BrepModel& body,
|
||
int face_id, double delta);
|
||
|
||
/**
|
||
* @brief 面移动:对指定面施加刚体变换
|
||
*
|
||
* 通过 4x4 仿射变换矩阵移动面。检测移动后面是否
|
||
* 与邻面相交(产生自交),相交时返回错误。
|
||
*
|
||
* @param body 输入 B-Rep 实体
|
||
* @param face_id 目标面索引
|
||
* @param transform 刚体变换矩阵(仅平移+旋转,不含缩放)
|
||
* @return DirectModelingResult 含新模型和诊断
|
||
*
|
||
* @note 非均匀缩放会导致法线失真;使用 Transform3D 时建议只用平移+旋转
|
||
* @note 移动后检测面是否穿透邻面(通过法线方向变化判断)
|
||
*
|
||
* @code{.cpp}
|
||
* auto box = make_box(2, 2, 2);
|
||
* auto T = translate(Vector3D(0.5, 0, 0)) * rotate_z(M_PI / 4);
|
||
* auto result = move_face(box, 0, T);
|
||
* @endcode
|
||
*/
|
||
[[nodiscard]] DirectModelingResult move_face(const BrepModel& body,
|
||
int face_id,
|
||
const core::Transform3D& transform);
|
||
|
||
/**
|
||
* @brief 面替换:用新曲面替换面的现有曲面
|
||
*
|
||
* 新曲面与原面必须有兼容的边界:新曲面的四条边界
|
||
* 应近似匹配面的四条边界边。不兼容时操作失败。
|
||
*
|
||
* @param body 输入 B-Rep 实体
|
||
* @param face_id 目标面索引
|
||
* @param new_surface 替换用的新 NURBS 曲面
|
||
* @return DirectModelingResult 含新模型和诊断
|
||
*
|
||
* @note 仅替换曲面的几何定义,拓扑结构不变
|
||
* @note 新曲面参数域应与原曲面兼容(边界对应对齐)
|
||
*
|
||
* @code{.cpp}
|
||
* auto box = make_box(2, 2, 2);
|
||
* NurbsSurface curved_surf = ...; // 曲面边界与 box 顶面匹配
|
||
* auto result = replace_face(box, 0, curved_surf);
|
||
* @endcode
|
||
*/
|
||
[[nodiscard]] DirectModelingResult replace_face(const BrepModel& body,
|
||
int face_id,
|
||
const curves::NurbsSurface& new_surface);
|
||
|
||
/**
|
||
* @brief 推拉操作:沿法线挤出(正值)或切除(负值)
|
||
*
|
||
* 正值 distance:面沿法向向外挤出,自动创建四侧面
|
||
* 闭合新体积。等同于"挤出添加材料"。
|
||
*
|
||
* 负值 distance:面沿法向向内凹陷,邻面自动延伸
|
||
* 封闭。等同于"切除材料"。
|
||
*
|
||
* @param body 输入 B-Rep 实体
|
||
* @param face_id 目标面索引
|
||
* @param distance 推拉距离(正=挤出加材料,负=切除减材料)
|
||
* @return DirectModelingResult 含新模型和诊断
|
||
*
|
||
* @pre distance 的绝对值不超过实体在法线方向的最小厚度
|
||
*
|
||
* @code{.cpp}
|
||
* auto box = make_box(2, 2, 2);
|
||
* // 顶面挤出 1.0 → 凸台
|
||
* auto pad = push_pull(box, 0, 1.0);
|
||
* // 顶面切除 0.5 → 凹陷
|
||
* auto pocket = push_pull(box, 0, -0.5);
|
||
* @endcode
|
||
*/
|
||
[[nodiscard]] DirectModelingResult push_pull(const BrepModel& body,
|
||
int face_id, double distance);
|
||
|
||
/**
|
||
* @brief 面等距偏移:将面均匀偏移指定距离
|
||
*
|
||
* 面等距偏移与 tweak_face 的区别:
|
||
* - offset_face 保证偏移后曲面与原曲面处处等距(等距曲面)
|
||
* - tweak_face 是近似偏移(沿平均法向移动顶点)
|
||
*
|
||
* 对于平面,两者等价。对于曲面,offset_face 生成
|
||
* 真正的等距曲面。
|
||
*
|
||
* @param body 输入 B-Rep 实体
|
||
* @param face_id 目标面索引
|
||
* @param offset_distance 等距偏移距离(正=向外,负=向内)
|
||
* @return DirectModelingResult 含新模型和诊断
|
||
*
|
||
* @warning 自由曲面等距偏移可能产生自交,需检查结果有效性
|
||
*
|
||
* @code{.cpp}
|
||
* auto sphere = make_sphere(3.0);
|
||
* auto result = offset_face(sphere, 0, 0.2); // 面均匀外扩 0.2
|
||
* @endcode
|
||
*/
|
||
[[nodiscard]] DirectModelingResult offset_face(const BrepModel& body,
|
||
int face_id, double offset_distance);
|
||
|
||
// ═══════════════════════════════════════════════════════════
|
||
// Advanced Direct Modeling Operations — v5.4
|
||
// ═══════════════════════════════════════════════════════════
|
||
|
||
/**
|
||
* @brief 高级拔模 — 面按拔模方向和角度倾斜
|
||
*
|
||
* 将指定面相对于拔模方向旋转到目标拔模角。
|
||
* 面的与邻面共享的边作为铰链轴(hinge line)。
|
||
*
|
||
* 对标 Parasolid Draft Face。
|
||
*
|
||
* @param body 输入 B-Rep 实体
|
||
* @param face_id 目标面索引
|
||
* @param angle_deg 拔模角度(度),> 0 且 < 90
|
||
* @param pull_dir 拔模方向(通常是模具开模方向)
|
||
* @return DirectModelingResult 含新模型和诊断
|
||
*
|
||
* @pre angle_deg 必须在 (0, 90) 度范围内
|
||
* @pre pull_dir 不能与面法向量平行
|
||
*
|
||
* @code{.cpp}
|
||
* auto box = make_box(10, 5, 3);
|
||
* // 面 0 向 Z+ 方向拔模 5 度
|
||
* auto result = draft_face_advanced(box, 0, 5.0, Vector3D::UnitZ());
|
||
* @endcode
|
||
*/
|
||
[[nodiscard]] DirectModelingResult draft_face_advanced(const BrepModel& body,
|
||
int face_id, double angle_deg,
|
||
const core::Vector3D& pull_dir);
|
||
|
||
/**
|
||
* @brief 非均匀缩放体
|
||
*
|
||
* 沿 X/Y/Z 轴以独立缩放因子缩放整个 B-Rep 实体。
|
||
* 对标 Parasolid Scale Body (non-uniform)。
|
||
*
|
||
* 均匀缩放时(所有因子相等)使用 Transform3D 加速。
|
||
* 非均匀缩放时逐个顶点变换并重建曲面控制点网格。
|
||
*
|
||
* @param body 输入 B-Rep 实体
|
||
* @param factors 各轴缩放因子 (sx, sy, sz),均必须 > 0
|
||
* @return DirectModelingResult 含缩放后的新模型
|
||
*
|
||
* @pre factors.x() > 0, factors.y() > 0, factors.z() > 0
|
||
*
|
||
* @code{.cpp}
|
||
* auto box = make_box(10, 5, 3);
|
||
* // X 方向拉伸 2x, Y/Z 不变
|
||
* auto stretched = scale_body(box, Vector3D(2.0, 1.0, 1.0));
|
||
* @endcode
|
||
*/
|
||
[[nodiscard]] DirectModelingResult scale_body(const BrepModel& body,
|
||
const core::Vector3D& factors);
|
||
|
||
/**
|
||
* @brief 镜像体
|
||
*
|
||
* 关于指定平面镜像整个 B-Rep 实体。
|
||
* 对标 Parasolid Mirror Body。
|
||
*
|
||
* 算法:
|
||
* 1. 所有顶点关于镜像平面对称映射
|
||
* 2. 曲面控制点网格镜像(保持参数化方向)
|
||
* 3. 环方向反转以保持正确的面朝向
|
||
*
|
||
* @param body 输入 B-Rep 实体
|
||
* @param plane_point 镜像平面上的一点
|
||
* @param plane_normal 镜像平面法向量
|
||
* @return DirectModelingResult 含镜像体
|
||
*
|
||
* @pre plane_normal 不能为零向量
|
||
*
|
||
* @code{.cpp}
|
||
* auto box = make_box(10, 5, 3);
|
||
* // 关于 YZ 平面 (x=0) 镜像
|
||
* auto mirrored = mirror_body(box, Point3D(0,0,0), Vector3D::UnitX());
|
||
* @endcode
|
||
*/
|
||
[[nodiscard]] DirectModelingResult mirror_body(const BrepModel& body,
|
||
const core::Point3D& plane_point,
|
||
const core::Vector3D& plane_normal);
|
||
|
||
// ═══════════════════════════════════════════════════════════
|
||
// Direct Modeling Enhancements — v5.5
|
||
// ═══════════════════════════════════════════════════════════
|
||
|
||
/**
|
||
* @brief 填孔:用边环定义的边界填充孔洞,生成新面
|
||
*
|
||
* 给定一个由边 ID 组成的闭合边环,在 B-Rep 实体中创建一个
|
||
* 新的平面面来填补孔洞。该操作对于修复导入的 STEP/IGES
|
||
* 文件中的缺失面非常有用。
|
||
*
|
||
* 算法:
|
||
* 1. 检测边环是否封闭(首尾相连)
|
||
* 2. 采样边环上的点
|
||
* 3. 最小二乘拟合平面
|
||
* 4. 创建平面 NURBS 曲面
|
||
* 5. 构建裁剪曲面并插入 B-Rep 面
|
||
*
|
||
* @param body 输入 B-Rep 实体(含有孔洞边界边环)
|
||
* @param edge_loop 构成孔洞边界的边 ID 数组
|
||
* @return DirectModelingResult
|
||
* - result.success: 操作是否成功
|
||
* - result.body: 含新面的模型
|
||
* - result.affected_faces: 新增面的 face_id
|
||
*
|
||
* @pre edge_loop.size() >= 3
|
||
* @pre 边环必须首尾相连形成闭合环
|
||
*
|
||
* @code{.cpp}
|
||
* // 用 4 条边构成的孔洞边界填充孔
|
||
* auto box_with_hole = ...; // 导入的有孔模型
|
||
* std::vector<int> hole_edges = {10, 11, 12, 13};
|
||
* auto result = fill_hole(box_with_hole, hole_edges);
|
||
* if (result.success) {
|
||
* int new_face = result.affected_faces;
|
||
* }
|
||
* @endcode
|
||
*/
|
||
[[nodiscard]] DirectModelingResult fill_hole(const BrepModel& body,
|
||
const std::vector<int>& edge_loop);
|
||
|
||
/**
|
||
* @brief 拉伸面对齐到目标面
|
||
*
|
||
* 将指定面沿其法向拉伸,直到与目标面贴合。
|
||
* 内部调用 push_pull 实现推拉,并自动计算所需距离。
|
||
*
|
||
* 算法:
|
||
* 1. 计算当前面到目标面的距离(沿当前面法向)
|
||
* 2. 调用 push_pull 拉伸到目标面距离
|
||
* 3. 检测拉伸后与目标面是否贴合(距离 < 容差)
|
||
*
|
||
* @param body 输入 B-Rep 实体
|
||
* @param face_id 被拉伸的面索引
|
||
* @param target_face_id 目标面对齐到的面索引
|
||
* @return DirectModelingResult
|
||
* - result.success: 是否成功贴合
|
||
* - result.body: 拉伸后的模型
|
||
*
|
||
* @pre face_id 和 target_face_id 均在有效范围内
|
||
* @pre face_id != target_face_id
|
||
* @pre 面应为平面(非平面会产生警告)
|
||
*
|
||
* @code{.cpp}
|
||
* auto box = make_box(10, 5, 3);
|
||
* // 将面 0 拉伸到与面 1 对齐
|
||
* auto result = pull_up_to_face(box, 0, 1);
|
||
* @endcode
|
||
*/
|
||
[[nodiscard]] DirectModelingResult pull_up_to_face(const BrepModel& body,
|
||
int face_id,
|
||
int target_face_id);
|
||
|
||
// ═══════════════════════════════════════════════════════════
|
||
// Direct Modeling Enhancements — v5.6
|
||
// ═══════════════════════════════════════════════════════════
|
||
|
||
/**
|
||
* @brief 相切联动拉伸:拉伸面并保持相邻面相切
|
||
*
|
||
* 将指定面沿其法向拉伸,同时检测并调整相邻面,使其
|
||
* 在共享边处保持与拉伸面的相切关系(G1 连续)。
|
||
*
|
||
* 非相切邻面保持原有行为(仅共享边处适配新位置)。
|
||
*
|
||
* 算法:
|
||
* 1. 检测相邻面(与目标面共享边的面)
|
||
* 2. 对每个相邻面检测 G1 连续性
|
||
* - 平面面:通过法向量平行性判定(dot > 0.9999)
|
||
* - 曲面:通过 surface_continuity 检测
|
||
* 3. 执行 push_pull 拉伸目标面
|
||
* 4. 对 G1 相邻面,将其非共享顶点沿目标面法向偏移相同距离
|
||
* 以保持相切关系
|
||
*
|
||
* @param body 输入 B-Rep 实体
|
||
* @param face_id 被拉伸的面索引
|
||
* @param distance 拉伸距离(正=挤出加材料,负=切除减材料)
|
||
* @return DirectModelingResult
|
||
* - result.body: 拉伸后保持相切的新模型
|
||
* - result.affected_faces: 受影响的面数(含调整的相邻面)
|
||
* - result.new_faces: 新创建的侧面数
|
||
*
|
||
* @pre face_id 在 [0, body.num_faces()) 范围内
|
||
* @pre 实体应为封闭水密体
|
||
* @pre distance 的绝对值不超过实体在法线方向的最小厚度
|
||
*
|
||
* @code{.cpp}
|
||
* auto box = make_box(10, 5, 3);
|
||
* // 拉伸顶面 2.0,相切邻面自动联动
|
||
* auto result = tangent_pull(box, 0, 2.0);
|
||
* @endcode
|
||
*/
|
||
[[nodiscard]] DirectModelingResult tangent_pull(const BrepModel& body,
|
||
int face_id, double distance);
|
||
|
||
} // namespace vde::brep
|