Files
ViewDesignEngine/include/vde/brep/direct_modeling.h
T
茂之钳 5bfcbcb8e9
CI / Build & Test (push) Failing after 39s
CI / Release Build (push) Failing after 33s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled
feat(v1.0.1): tangent_pull + error codes + version + license management
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)
2026-07-27 21:54:46 +08:00

389 lines
15 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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