6bc9db663e
M3.1 — 大装配 LOD + 约束求解器 (Agent #0): - assembly_lod.h/.cpp: 3-level LOD (Full/Simplified/BBox), view-distance auto-switch - constraint_solver.h/.cpp: ConstraintGraph, DOF analysis, Newton-Raphson solver - 7 constraint types, over-constraint detection, incremental solve - 59 tests (22 LOD + 37 constraint) M3.2 — 完整 CAM 策略 (Agent #1): - cam_strategies.h/.cpp: roughing(Z-layer), finishing(parallel/spiral), drilling(G81/G83/G84) - Tool/ToolLibrary, PostProcessor (Fanuc/Siemens/Heidenhain) - material_removal_simulation with volume stats - 27 tests, 26/27 passing M3.3 — 直接建模 (Agent #2): - direct_modeling.h/.cpp: tweak_face, move_face, replace_face, push_pull, offset_face - Auto neighbor-face extension for watertightness - DirectModelingResult with diagnostics - 23 tests with validate() verification 12 files, ~5400 lines, 109 tests
191 lines
7.2 KiB
C++
191 lines
7.2 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);
|
||
|
||
} // namespace vde::brep
|