feat(v10): tolerant modeling + Class-A deep + hex mesh + constraint solver + drawing standards
CI / Build & Test (push) Failing after 31s
CI / Release Build (push) Failing after 31s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled

v10.1 — Tolerant Modeling (对标 Parasolid):
- tolerant_modeling.h/.cpp: TolerantVertex/Edge, merge_within_tolerance (BFS)
- gap_bridging (free edge detection → triangle filling)
- overlap_resolution (normal+centroid+AABB detection)
- tolerant_boolean (heal→degenerate detect→boolean→heal) + BooleanDiagnostic
- import_heal_pipeline (STEP one-shot repair)
- ssi_boolean enhanced: coplanar/collinear/tangent detection, GMP on all classify paths
- step_import enhanced: broken file recovery, non-standard entity mapping, diagnostic report
- 30 tests, 4112 total lines

v10.2 — Class-A Deep + Hex Mesh (对标 CGM + ANSYS):
- class_a_surfacing enhanced: g3_blend_with_constraints, surface_energy_minimization
- curvature_continuity_optimization, reflection_line_discontinuity (+720 lines)
- fea_mesh enhanced: mapped_hex, submapped_hex, multi_block_hex (TFI), hex_quality_optimization (+522 lines)
- 22 tests

v10.3 — Constraint Solver + Drawing Standards (对标 D-Cubed + AutoCAD):
- constraint_solver enhanced: DOFAnalyzer, RedundancyDetector, ConstraintPropagator, KinematicChainSolver
- drawing_standards.h/.cpp: IsoStandard (ISO 128/129), AnsiStandard (ASME Y14.5), JisStandard (JIS B 0001)
- 12 tests

12 files, ~5000 lines, 64 tests. Target: 87% → 93%
This commit is contained in:
茂之钳
2026-07-27 00:33:30 +08:00
parent ddcfe01cba
commit 63fba5389a
21 changed files with 6333 additions and 468 deletions
+246
View File
@@ -24,6 +24,7 @@
#include <unordered_set>
#include <functional>
#include <optional>
#include <tuple>
namespace vde::brep {
@@ -350,6 +351,251 @@ private:
const Eigen::VectorXd& f) const;
};
// ═══════════════════════════════════════════════════════════
// DOFAnalyzer — 详细自由度分析器
// ═══════════════════════════════════════════════════════════
/// DOF 分解方向描述
struct DOFDirection {
/// 平移方向:x, y, z 是否被约束
bool tx_free = true, ty_free = true, tz_free = true;
/// 旋转方向:绕 x, y, z 是否被约束
bool rx_free = true, ry_free = true, rz_free = true;
};
/// DOF 分析详细结果
struct DOFDetailedResult {
int node_index = -1;
std::string node_name;
int translational_dof_remaining = 3; ///< 剩余平移自由度
int rotational_dof_remaining = 3; ///< 剩余旋转自由度
DOFDirection free_directions; ///< 哪些方向未约束
std::vector<int> constraints_used; ///< 涉及约束索引
bool is_fully_constrained = false; ///< 完全约束
bool is_over_constrained = false; ///< 过度约束
std::string summary; ///< 可读摘要
};
/**
* @brief 零件自由度分析器
*
* 将每个零件的 6 个 DOF 分解为平移(3)和旋转(3),
* 根据约束类型计算每个方向上的自由/约束状态。
*
* @ingroup brep
*/
class DOFAnalyzer {
public:
/// 执行全图 DOF 分析
[[nodiscard]] std::vector<DOFDetailedResult> analyze(const ConstraintGraph& graph) const;
/// 获取单个零件的自由方向
[[nodiscard]] DOFDirection free_directions(const ConstraintGraph& graph, int node_idx) const;
/// 列出零件剩余的自由运动方向描述
[[nodiscard]] std::vector<std::string> free_direction_names(
const ConstraintGraph& graph, int node_idx) const;
/// 分析所有约束后的全局 DOF 状态
[[nodiscard]] std::string global_dof_summary(const ConstraintGraph& graph) const;
private:
/// 根据约束类型,返回对 node_a 的平移/旋转约束影响
static void constraint_impact(ConstraintType type, bool is_node_a,
int& trans_count, int& rot_count,
DOFDirection& dir);
};
// ═══════════════════════════════════════════════════════════
// RedundancyDetector — 冗余约束检测器
// ═══════════════════════════════════════════════════════════
/// 冗余约束建议
struct RedundancySuggestion {
int constraint_index = -1; ///< 冗余约束索引
int node_index = -1; ///< 受影响的节点
std::string reason; ///< 原因描述
int dof_impact = 0; ///< 移除后释放的 DOF 数
std::string action; ///< 建议操作(删除/修改/保留)
};
/// 约束冲突信息
struct ConstraintConflict {
int constraint_a = -1;
int constraint_b = -1;
int shared_node = -1; ///< 冲突所在的公共节点
std::string description;
};
/**
* @brief 冗余约束检测器
*
* 检测约束图中的冗余约束和约束冲突:
* - 类型级冗余:同节点对之间存在多个相同类型的约束
* - 拓扑级冗余:约束消除的 DOF 总量超出 6
* - 语义冲突:约束类型相互矛盾(如 Coincident + Distance ≠ 0
*
* @ingroup brep
*/
class RedundancyDetector {
public:
/// 全图检测冗余
[[nodiscard]] std::vector<RedundancySuggestion> detect(const ConstraintGraph& graph) const;
/// 针对特定节点检测
[[nodiscard]] std::vector<RedundancySuggestion> detect_for_node(
const ConstraintGraph& graph, int node_idx) const;
/// 检测约束冲突
[[nodiscard]] std::vector<ConstraintConflict> detect_conflicts(
const ConstraintGraph& graph) const;
/// 生成可读报告
[[nodiscard]] std::string report(const ConstraintGraph& graph) const;
private:
/// 判断两个约束是否冲突
static bool are_conflicting(ConstraintType a, ConstraintType b);
};
// ═══════════════════════════════════════════════════════════
// ConstraintPropagator — 约束传播器
// ═══════════════════════════════════════════════════════════
/// 传播结果
struct PropagateResult {
bool success = false;
std::vector<int> affected_nodes; ///< 受影响的节点索引
std::vector<int> affected_constraints; ///< 受影响的约束索引
std::vector<std::string> changes; ///< 变更描述
std::string message; ///< 可读消息
};
/**
* @brief 约束传播器
*
* 修改一个约束后,沿依赖图传播更新:
* 1. 计算图拓扑排序(BFS 从固定节点出发)
* 2. 标记受影响的下游节点
* 3. 增量重新求解受影响的部分
*
* @ingroup brep
*/
class ConstraintPropagator {
public:
/**
* @brief 修改约束并传播
*
* @param graph 约束图
* @param assembly 装配体
* @param constraint_idx 被修改约束索引
* @param new_value 新值
* @param new_type 新类型(可选:不修改则设为相同值)
* @param solver 求解器(可选:用于增量求解)
* @return 传播结果
*/
[[nodiscard]] PropagateResult propagate(
ConstraintGraph& graph,
Assembly& assembly,
int constraint_idx,
double new_value,
ConstraintType new_type,
NewtonRaphsonSolver* solver = nullptr);
/// 计算依赖传播顺序(BFS
[[nodiscard]] std::vector<int> compute_dependency_order(
const ConstraintGraph& graph, int start_node) const;
/// 获取约束之间的依赖深度
[[nodiscard]] int dependency_depth(
const ConstraintGraph& graph, int a_idx, int b_idx) const;
};
// ═══════════════════════════════════════════════════════════
// KinematicChainSolver — 闭环机构求解器
// ═══════════════════════════════════════════════════════════
/// 运动学链的连杆描述
struct ChainLink {
int node_index = -1; ///< 对应的约束图节点
double length = 1.0; ///< 连杆长度(相对于前一节点)
double twist_angle = 0.0; ///< 扭转角(弧度)
double offset = 0.0; ///< 偏距
std::string label; ///< 标签
};
/// 闭环条件
struct LoopClosure {
int start_node = -1; ///< 环路起始节点
int end_node = -1; ///< 环路终止节点
ConstraintType closure_type = ConstraintType::Coincident;
double closure_value = 0.0; ///< 目标间距/角度
double tolerance = 1e-6; ///< 容差
};
/// 运动学求解结果
struct KinematicResult {
bool solved = false;
int iterations = 0;
double final_error = 0.0;
std::vector<double> joint_angles; ///< 求解出的关节角度
std::vector<core::Transform3D> poses; ///< 求解出的位姿
std::string message;
};
/**
* @brief 闭环机构求解器
*
* 求解闭环运动学链的关节角度:
* - 将闭环约束表示为非线性方程组
* - 使用 Newton-Raphson 迭代求解
* - 支持任意长度的串行链 + 末端闭合条件
*
* @ingroup brep
*/
class KinematicChainSolver {
public:
/** @brief DH 参数法:单个连杆变换(公开,供测试使用) */
static core::Transform3D dh_transform(double theta, double d,
double a, double alpha);
/** @brief 求解闭环运动学链 */
[[nodiscard]] KinematicResult solve_closed_chain(
ConstraintGraph& graph,
Assembly& assembly,
const std::vector<ChainLink>& links,
const LoopClosure& closure);
/** @brief 前向运动学:给定关节角 → 位姿 */
[[nodiscard]] std::vector<core::Transform3D> forward_kinematics(
const Assembly& assembly,
const std::vector<ChainLink>& links,
const std::vector<double>& joint_angles) const;
/** @brief 逆向运动学:给定目标位姿 → 关节角 */
[[nodiscard]] std::vector<double> inverse_kinematics(
const Assembly& assembly,
const std::vector<ChainLink>& links,
const core::Transform3D& target_pose) const;
/** @brief 检查链是否可达 */
[[nodiscard]] bool is_reachable(
const std::vector<ChainLink>& links,
const core::Point3D& target) const;
private:
/// DH 参数法:单个连杆变换
static core::Transform3D dh_transform(double theta, double d,
double a, double alpha);
/// 评估闭环残差
static double evaluate_closure(
const Assembly& assembly,
const std::vector<ChainLink>& links,
const std::vector<double>& angles,
const LoopClosure& closure);
};
// ═══════════════════════════════════════════════════════════
// 向后兼容 API(保留旧接口)
// ═══════════════════════════════════════════════════════════
+377
View File
@@ -0,0 +1,377 @@
#pragma once
/**
* @file drawing_standards.h
* @brief 工程制图标准 — ISO 128/129, ASME Y14.5, JIS B 0001
*
* 提供三大国际制图标准的完整规则配置:
* - IsoStandard: ISO 128/129(线型/字体/箭头/公差格式)
* - AnsiStandard: ASME Y14.5-2018(尺寸与公差标注)
* - JisStandard: JIS B 0001(机械制图)
*
* 支持一键应用标准(apply_standard/apply_standard_by_name
* 自动配置尺寸样式、视图投影角度(第一角/第三角)
*
* @ingroup brep
*/
#include <string>
#include <vector>
#include <sstream>
namespace vde::brep {
// ═══════════════════════════════════════════════════════════
// 线型枚举
// ═══════════════════════════════════════════════════════════
/// 标准线型(对应 ISO 128-20 / ASME Y14.2 / JIS B 0001
enum class LineType {
Continuous, ///< 实线(可见轮廓线)— ISO 01.1 / ASME visible
Dashed, ///< 虚线(隐藏线)— ISO 02.1 / ASME hidden
Chain, ///< 点划线(中心线)— ISO 04.1 / ASME center
DoubleChain, ///< 双点划线(假想线/相邻零件轮廓)— ISO 05.1
Dotted, ///< 点线 — ISO 07.1
ContinuousThin, ///< 细实线(尺寸线/指引线/剖面线)
ContinuousThick, ///< 粗实线(可见轮廓线,主视图)
ContinuousFreehand ///< 徒手线(断裂线)
};
/// 线型名称
[[nodiscard]] inline const char* line_type_name(LineType lt) {
switch (lt) {
case LineType::Continuous: return "Continuous";
case LineType::Dashed: return "Dashed";
case LineType::Chain: return "Chain";
case LineType::DoubleChain: return "DoubleChain";
case LineType::Dotted: return "Dotted";
case LineType::ContinuousThin: return "ContinuousThin";
case LineType::ContinuousThick: return "ContinuousThick";
case LineType::ContinuousFreehand: return "ContinuousFreehand";
}
return "Unknown";
}
// ═══════════════════════════════════════════════════════════
// 样式枚举
// ═══════════════════════════════════════════════════════════
/// 字体样式
enum class FontStyle {
Normal,
Italic,
Bold
};
/// 箭头样式
enum class ArrowStyle {
Filled, ///< 实心箭头(ISO/ANSI 默认)
Open, ///< 空心箭头(ISO 128-22 允许)
Oblique, ///< 斜线标记(建筑图常用)
Architectural, ///< 建筑标记(短线)
Dot, ///< 圆点
Integral ///< 积分标记(原点指示)
};
/// 公差格式
enum class ToleranceFormat {
None, ///< 无公差(仅基本尺寸)
Symmetric, ///< ±x 对称公差
Deviation, ///< 上下偏差(+x / -y
Limits, ///< 极限尺寸(Max / Min
Basic, ///< 基本尺寸(方框 — ASME Y14.5 理论正确尺寸)
Reference ///< 参考尺寸(圆括号 — 仅供参考)
};
/// 投影角度
enum class ProjectionAngle {
FirstAngle, ///< 第一角投影(ISO / JIS / GB
ThirdAngle ///< 第三角投影(ASME / ANSI
};
/// 尺寸单位
enum class DimensionUnit {
Millimeter,
Inch,
Centimeter
};
// ═══════════════════════════════════════════════════════════
// DimensionStyle — 尺寸样式
// ═══════════════════════════════════════════════════════════
/// 完整的尺寸标注样式配置
struct DimensionStyle {
// ── 文字 ──
double text_height = 3.5; ///< 文字高度(mm
FontStyle text_font = FontStyle::Normal;
double text_width_factor = 0.7; ///< 文字宽高比
double text_gap = 0.5; ///< 文字与尺寸线间距
// ── 箭头 ──
double arrow_length = 3.5; ///< 箭头长度(mm
double arrow_width = 1.0; ///< 箭头宽度(mm
double arrow_angle = 15.0; ///< 箭头夹角(°)
ArrowStyle arrow_style = ArrowStyle::Filled;
// ── 线宽(mm)─ 对应 ISO 128 线宽系列 ──
double line_width_thin = 0.25; ///< 细线(尺寸线/指引线)
double line_width_thick = 0.50; ///< 粗线(可见轮廓线)
double line_width_medium = 0.35; ///< 中线(隐藏线)
// ── 尺寸界线 ──
double extension_line_offset = 1.0; ///< 尺寸界线起点偏移(mm)
double extension_line_extend = 2.0; ///< 尺寸界线超出(mm
// ── 线型 ──
LineType visible_line = LineType::Continuous;
LineType hidden_line = LineType::Dashed;
LineType center_line = LineType::Chain;
LineType phantom_line = LineType::DoubleChain;
LineType dimension_line = LineType::ContinuousThin;
LineType leader_line = LineType::ContinuousThin;
LineType section_line = LineType::ContinuousThin;
LineType cutting_plane = LineType::DoubleChain;
// ── 公差 ──
ToleranceFormat tolerance_format = ToleranceFormat::None;
int decimal_places = 2; ///< 小数位数
double tolerance_upper = 0.0; ///< 上偏差(mm
double tolerance_lower = 0.0; ///< 下偏差(mm
bool show_trailing_zeros = true; ///< 显示末尾零(ASME 要求)
bool use_basic_box = true; ///< 基本尺寸方框
// ── 投影 ──
ProjectionAngle projection_angle = ProjectionAngle::FirstAngle;
// ── 单位 ──
DimensionUnit unit = DimensionUnit::Millimeter;
bool show_unit_symbol = false; ///< 是否标注 mm 符号
// ── 显示控制 ──
bool show_hidden_lines = true;
bool show_center_lines = true;
bool show_center_marks = true;
bool show_threads_simplified = true;
double center_mark_size = 3.0;
double gap_between_dimension_lines = 7.0; ///< 尺寸线间距
// ── 页面 ──
double drawing_scale = 1.0;
};
// ═══════════════════════════════════════════════════════════
// StandardOptions — 标准选项
// ═══════════════════════════════════════════════════════════
/// 应用标准时的配置选项
struct StandardOptions {
std::string paper_size = "A4"; ///< 图纸大小 A0-A4, Letter等
double scale = 1.0; ///< 缩放比例
bool apply_to_dimensions = true; ///< 应用到尺寸标注
bool apply_to_views = true; ///< 应用到视图配置
std::string revision = "A"; ///< 修订版本
std::string title; ///< 图纸标题
std::string author; ///< 作者
};
// ═══════════════════════════════════════════════════════════
// IsoStandard — ISO 128/129 标准
// ═══════════════════════════════════════════════════════════
/**
* @brief ISO 128/129 制图标准
*
* ISO 128: 技术制图的一般表示原则
* - Part 20: 线型基本规定(01-15系列线型编号)
* - Part 22: 指引线和参考线
* - Part 24: 机械工程制图用线
* - Part 30: 视图基本规定
* - Part 34: 机械工程制图视图
* - Part 40: 剖视图和断面图
* - Part 50: 尺寸标注
*
* ISO 129: 技术制图 — 尺寸和公差的标注方法
* - 线性尺寸标注规则
* - 角度尺寸标注规则
* - 圆弧半径/直径标注规则
*
* 默认配置:
* - 第一角投影
* - 文字高度 3.5mmISO 3098 B型)
* - 线宽系列 0.25/0.35/0.50mm
* - 空心箭头或实心填充箭头
*/
class IsoStandard {
public:
/// 创建 ISO 标准样式
[[nodiscard]] static DimensionStyle create_style();
/// ISO 标准描述
[[nodiscard]] static std::string description();
/// ISO 128 线宽系列(mm):0.13, 0.18, 0.25, 0.35, 0.5, 0.7, 1.0, 1.4, 2.0
/// @param group 线宽组索引 0-8
[[nodiscard]] static double line_width_group(int group);
/// ISO 128 线型编号名称
/// @param lt 线型
/// @return ISO 128 编号描述(如 "01.1" 表示连续细线)
[[nodiscard]] static std::string line_type_iso_name(LineType lt);
/// ISO 3098 字体尺寸系列
[[nodiscard]] static std::vector<double> text_height_series();
/// ISO 7200 标题栏尺寸(A4
[[nodiscard]] static std::string title_block_spec();
};
// ═══════════════════════════════════════════════════════════
// AnsiStandard — ASME Y14.5 标准
// ═══════════════════════════════════════════════════════════
/**
* @brief ASME Y14.5-2018 尺寸与公差标注标准
*
* 核心规则:
* - 第三角投影
* - 英寸/毫米双单位支持
* - 包容原则(Rule #1: Envelope Principle
* - 不依赖原则的独立标注(Rule #2)
* - MMC/LMC/RFS 材料条件修饰符
* - 基准参考框架(DRF)6个自由度约束
* - 特征控制框格式
*
* Y14.5 公差原则:
* - Form controls: Straightness, Flatness, Circularity, Cylindricity
* - Orientation: Parallelism, Perpendicularity, Angularity
* - Location: Position, Concentricity, Symmetry
* - Runout: Circular runout, Total runout
* - Profile: Profile of a line, Profile of a surface
*/
class AnsiStandard {
public:
/// 创建 ASME Y14.5 标准样式
[[nodiscard]] static DimensionStyle create_style();
/// ASME Y14.5 标准描述
[[nodiscard]] static std::string description();
/// 包容原则(Rule #1)是否默认启用
[[nodiscard]] static bool envelope_principle_default();
/// MMC(最大实体条件)是否默认标注
[[nodiscard]] static bool mmc_default();
/// ASME Y14.5 符号表
[[nodiscard]] static std::string gdt_symbols_summary();
/// 特征控制框格式规范
[[nodiscard]] static std::string feature_control_frame_format();
/// ASME Y14.2 线型对 ASME Y14.5 的映射
[[nodiscard]] static std::string line_type_spec(LineType lt);
};
// ═══════════════════════════════════════════════════════════
// JisStandard — JIS B 0001 标准
// ═══════════════════════════════════════════════════════════
/**
* @brief JIS B 0001 机械制图标准
*
* 核心规则:
* - 第一角投影(日本采用的投影法)
* - 公制单位(mm
* - 表面粗糙度符号(▽ 旧/JIS B 0601 → Ra/Rz 新)
* - 焊接符号(JIS Z 3021
* - 螺纹简化画法
* - JIS B 0401 公差等级
*
* 与 ISO 的对应关系:
* - JIS B 0001 积极向 ISO 128 靠拢
* - 尺寸标注规则与 ISO 129 基本一致
* - 表面粗糙度: JIS B 0601 (对应 ISO 1302)
*/
class JisStandard {
public:
/// 创建 JIS B 0001 标准样式
[[nodiscard]] static DimensionStyle create_style();
/// JIS B 0001 标准描述
[[nodiscard]] static std::string description();
/// JIS 表面粗糙度符号
[[nodiscard]] static std::string surface_texture_symbol();
/// JIS B 0001 推荐缩放比例
[[nodiscard]] static std::vector<double> preferred_scales();
/// JIS 公差等级对应(h7, H7 等)
[[nodiscard]] static std::string tolerance_grade_desc(const std::string& grade);
/// JIS B 0001 图纸尺寸系列(A0-A4 的毫米尺寸)
[[nodiscard]] static std::string paper_size_spec(const std::string& size_name);
};
// ═══════════════════════════════════════════════════════════
// Drawing(前向声明)
// ═══════════════════════════════════════════════════════════
struct DrawSegment;
struct ProjectionView;
/// 制图标准命名空间
namespace drawing {
/// 应用标准后的制图上下文
struct DrawingContext {
std::vector<ProjectionView> views;
DimensionStyle style;
StandardOptions options;
std::string standard_name; ///< "ISO", "ANSI", "JIS"
ProjectionAngle view_projection = ProjectionAngle::FirstAngle;
};
/**
* @brief 一键应用标准到制图
*
* 根据标准自动配置:
* - 尺寸样式
* - 视图投影角度(第一角/第三角)
* - 线型/字体/箭头样式
*
* @param views 制图视图
* @param style 样式配置
* @param options 标准选项
* @return DrawingContext 应用后的制图上下文
*/
[[nodiscard]] DrawingContext apply_standard(
const std::vector<ProjectionView>& views,
const DimensionStyle& style,
const StandardOptions& options);
/**
* @brief 按标准名称一键应用
*
* @param views 制图视图
* @param standard_name "ISO", "ANSI", "JIS"
* @param options 标准选项
* @return DrawingContext
*/
[[nodiscard]] DrawingContext apply_standard_by_name(
const std::vector<ProjectionView>& views,
const std::string& standard_name,
const StandardOptions& options);
/// 验证样式是否符合指定标准
[[nodiscard]] std::vector<std::string> validate_style(
const DimensionStyle& style, const std::string& standard_name);
/// 列出所有支持的标准
[[nodiscard]] std::vector<std::string> available_standards();
/// 获取标准描述
[[nodiscard]] std::string standard_description(const std::string& standard_name);
} // namespace drawing
} // namespace vde::brep
+44
View File
@@ -125,4 +125,48 @@ enum class StepError {
*/
[[nodiscard]] const std::string& step_last_error_message();
// ═══════════════════════════════════════════════════════════
// Import diagnostics (new in v3.3)
// ═══════════════════════════════════════════════════════════
/**
* @brief STEP 导入诊断报告
*
* 记录最近一次 STEP 导入的详细诊断信息,包括:
* - 跳过的损坏实体数
* - 跳过的非标实体数
* - 近似处理的曲面数
* - 从破损数据中恢复的面数
* - 警告消息列表
*/
struct StepImportReport {
int total_entities = 0; ///< 成功解析的实体总数
int skipped_damaged = 0; ///< 跳过的损坏实体数
int skipped_unsupported = 0; ///< 跳过的非标实体数
int approximated_surfaces = 0; ///< 近似处理的曲面数
int recovered_faces = 0; ///< 从破损数据中恢复的面数
std::vector<std::string> warnings; ///< 警告消息列表
/// 是否有任何异常
[[nodiscard]] bool has_issues() const {
return skipped_damaged > 0 || skipped_unsupported > 0 ||
approximated_surfaces > 0 || recovered_faces > 0 ||
!warnings.empty();
}
/// 生成可读的诊断报告
[[nodiscard]] std::string report() const;
};
/**
* @brief 获取最近一次 STEP 导入的诊断报告
*
* 线程安全:返回当前线程最近一次导入的诊断信息。
*
* @return StepImportReport 诊断报告
*
* @see step_last_error 获取错误码
*/
[[nodiscard]] StepImportReport step_import_diagnostic();
} // namespace vde::brep
+265
View File
@@ -0,0 +1,265 @@
#pragma once
/**
* @file tolerant_modeling.h
* @brief 容错建模 — 对标 Parasolid 的容差感知 B-Rep 操作
*
* 提供带容差范围的拓扑元素、自适应合并、间隙桥接、重叠面检测、
* 容差感知布尔运算和 STEP 导入一站式修复流水线。
*
* ## 核心能力
*
* 1. **TolerantVertex/TolerantEdge** — 带容差范围的拓扑元素
* 2. **merge_within_tolerance** — BFS 自适应顶点合并
* 3. **gap_bridging** — 间隙检测 + 自动三角填充
* 4. **overlap_resolution** — 重叠面检测 + 移除
* 5. **tolerant_boolean** — 容差感知布尔 (heal → boolean → heal)
* 6. **import_heal_pipeline** — STEP 导入一站式修复
*
* @ingroup brep
*/
#include "vde/brep/brep.h"
#include "vde/brep/brep_heal.h"
#include "vde/brep/tolerance.h"
#include "vde/core/point.h"
#include <string>
#include <vector>
#include <map>
#include <functional>
namespace vde::brep {
using core::Point3D;
using core::Vector3D;
using core::AABB3D;
// ═══════════════════════════════════════════════════════════
// TolerantVertex / TolerantEdge
// ═══════════════════════════════════════════════════════════
/**
* @brief 带容差范围的拓扑顶点
*
* 除了基本坐标外,额外存储:
* - tolerance: 该顶点的合并容差(可独立于全局配置)
* - merged_from: 若此顶点由多个原始顶点合并而来,记录合并数
* - is_degenerate: 标记退化顶点(如切触引起的退化边端点)
*/
struct TolerantVertex {
int id; ///< 顶点 ID(对应 BrepModel 中的索引)
Point3D point; ///< 三维坐标
double tolerance = 1e-6; ///< 该顶点的独立容差
int merged_from = 1; ///< 合并来源顶点数(1 = 未合并)
bool is_degenerate = false; ///< 是否属于退化拓扑
/// 从普通 TopoVertex 构造
static TolerantVertex from_topo(const TopoVertex& tv) {
TolerantVertex res;
res.id = tv.id;
res.point = tv.point;
res.tolerance = tv.tolerance;
return res;
}
};
/**
* @brief 带容差范围的拓扑边
*
* 额外存储:
* - tolerance: 边的曲线拟合容差
* - is_degenerate: 退化边(两端点重合或长度<tolerance
* - gap_flag: 标记此边参与间隙桥接
*/
struct TolerantEdge {
int id; ///< 边 ID
int v_start, v_end; ///< 端点顶点 ID
double tolerance = 1e-6; ///< 曲线拟合容差
bool is_degenerate = false; ///< 退化边(长度 < tolerance
bool gap_flag = false; ///< 参与间隙桥接
bool is_tangent_contact = false; ///< 切触边(奇异性处理)
/// 从普通 TopoEdge 构造
static TolerantEdge from_topo(const TopoEdge& te) {
TolerantEdge res;
res.id = te.id;
res.v_start = te.v_start;
res.v_end = te.v_end;
return res;
}
/// 判断是否为退化边
[[nodiscard]] bool check_degenerate(const BrepModel& body) const {
const auto& v0 = body.vertex(v_start);
const auto& v1 = body.vertex(v_end);
return (v1.point - v0.point).norm() < tolerance;
}
};
// ═══════════════════════════════════════════════════════════
// BooleanDiagnostic — 布尔运算失败面诊断报告
// ═══════════════════════════════════════════════════════════
/**
* @brief 布尔运算失败面的诊断信息
*
* 记录每个失败面的原因、位置和容差建议。
*/
struct FailedFaceInfo {
int face_id = -1; ///< 失败的面 ID
std::string reason; ///< 失败原因描述
int related_face_id = -1; ///< 关联面 ID(如有)
double suggested_tolerance = 0.0; ///< 建议的容差增大值
Point3D location; ///< 失败位置(近似)
bool is_coplanar = false; ///< 共面退化
bool is_tangent = false; ///< 切触退化
bool is_near_miss = false; ///< 接近但未精确相交
};
/**
* @brief 容差感知布尔运算的诊断结果
*
* 包含布尔运算的完整诊断信息,包括预处理/后处理统计、
* 失败面列表和建议修复方案。
*/
struct BooleanDiagnostic {
bool success = false; ///< 布尔运算是否成功
BrepModel result; ///< 结果模型
// ── 预处理统计 ──
int pre_heal_merged_vertices = 0; ///< 预处理合并的顶点数
int pre_heal_closed_gaps = 0; ///< 预处理闭合的间隙数
int pre_heal_removed_slivers = 0; ///< 预处理移除的退化面数
// ── 布尔运算统计 ──
int degeneracy_detections = 0; ///< 检测到的退化情况数
int coplanar_pairs = 0; ///< 共面对数量
int tangent_contacts = 0; ///< 切触对数量
int exact_predicate_upgrades = 0; ///< 升级到精确谓词的次数
// ── 后处理统计 ──
int post_heal_merged_vertices = 0; ///< 后处理合并的顶点数
int post_heal_bridged_gaps = 0; ///< 后处理桥接的间隙数
int post_heal_resolved_overlaps = 0;///< 后处理解决的重叠面数
// ── 失败诊断 ──
std::vector<FailedFaceInfo> failed_faces; ///< 失败面列表
std::string error_message; ///< 错误信息
/// 诊断是否包含失败面
[[nodiscard]] bool has_failures() const {
return !failed_faces.empty() || !error_message.empty();
}
/// 生成可读的诊断报告
[[nodiscard]] std::string report() const;
};
// ═══════════════════════════════════════════════════════════
// Public API
// ═══════════════════════════════════════════════════════════
/**
* @brief BFS 自适应顶点合并
*
* 与 heal_gaps 类似,但支持:
* - 每顶点独立容差(TolerantVertex::tolerance
* - 自适应容差:根据局部几何特征动态调整合并阈值
* - 退化顶点检测:标记切触产生的退化顶点
*
* @param body 待修复的 B-Rep 模型(原地修改)
* @param tolerance 全局合并容差,默认 1e-6
* @return 合并的顶点对数
*/
int merge_within_tolerance(BrepModel& body, double tolerance = 1e-6);
/**
* @brief 间隙检测 + 自动三角填充
*
* 检测面之间的微小间隙(距离 < max_gap),
* 自动生成三角填充面以桥接间隙。
*
* 算法:
* 1. 遍历所有边,查找自由边(仅属于一个面)
* 2. 对自由边配对:距离 < max_gap 的边视为间隙边界
* 3. 对每对自由边生成三角填充面
*
* @param body 待修复的 B-Rep 模型(原地修改)
* @param max_gap 最大间隙尺寸,默认 1e-4
* @return 填充的间隙数
*/
int gap_bridging(BrepModel& body, double max_gap = 1e-4);
/**
* @brief 重叠面检测 + 移除
*
* 检测并移除重叠/重复的面:
* 1. 计算每对面的法向量和位置相似度
* 2. 共面且重叠 → 保留面积较大的面,移除较小的面
* 3. 更新壳的面引用
*
* @param body 待修复的 B-Rep 模型(原地修改)
* @return 移除的重叠面数
*/
int overlap_resolution(BrepModel& body);
/**
* @brief 容差感知布尔运算
*
* 完整流水线:heal → boolean → heal
* 1. 预处理:对 A 和 B 分别执行 heal_topology
* 2. 退化检测:共面/共线/切触 → 记录诊断信息
* 3. 执行布尔运算(委托给 ssi_boolean_*
* 4. 后处理:gap_bridging + overlap_resolution + heal_topology
*
* @param a 第一个 B-Rep 实体
* @param b 第二个 B-Rep 实体
* @param op 布尔操作类型:0=union, 1=intersection, 2=difference
* @return BooleanDiagnostic 含结果模型和诊断信息
*/
[[nodiscard]] BooleanDiagnostic tolerant_boolean(
const BrepModel& a, const BrepModel& b, int op);
/**
* @brief STEP 导入一站式修复流水线
*
* 导入 STEP 数据后自动执行完整修复:
* 1. 导入 STEP(内部调用 import_step_from_string
* 2. 对每个 body 执行 merge_within_tolerance
* 3. 执行 gap_bridging 闭合微小间隙
* 4. 执行 overlap_resolution 移除重复面
* 5. 执行 heal_orientation 统一方向
* 6. 验证最终结果
*
* @param step_data STEP 文件内容(字符串形式)
* @return 修复后的 B-Rep 体列表
*
* @note 与直接调用 import_step_from_string() + heal_topology() 不同,
* 此函数按容错建模流水线顺序执行,包含 gap_bridging 和
* overlap_resolution 步骤。
*/
[[nodiscard]] std::vector<BrepModel> import_heal_pipeline(const std::string& step_data);
// ═══════════════════════════════════════════════════════════
// Internal utilities(跨编译单元可见)
// ═══════════════════════════════════════════════════════════
/// 检测两个面是否为退化共面对
[[nodiscard]] bool detect_coplanar_faces(
const BrepModel& body, int face_a, int face_b, double tolerance = 1e-6);
/// 检测两个面是否为退化切触对
[[nodiscard]] bool detect_tangent_contact(
const BrepModel& body, int face_a, int face_b, double tolerance = 1e-6);
/// 检测边是否为退化边(长度 < tolerance
[[nodiscard]] bool detect_degenerate_edge(
const BrepModel& body, int edge_id, double tolerance = 1e-6);
/// 构建 TolerantVertex 映射表
[[nodiscard]] std::vector<TolerantVertex> build_tolerant_vertices(
const BrepModel& body, double global_tolerance = 1e-6);
/// 构建 TolerantEdge 映射表
[[nodiscard]] std::vector<TolerantEdge> build_tolerant_edges(
const BrepModel& body, double global_tolerance = 1e-6);
} // namespace vde::brep
+202
View File
@@ -18,6 +18,7 @@
#include "vde/core/point.h"
#include <vector>
#include <array>
#include <tuple>
#include <utility>
#include <string>
#include <functional>
@@ -362,4 +363,205 @@ struct ShapeModificationResult {
const NurbsSurface& surface,
const std::vector<ShapeConstraint>& constraints);
// ═══════════════════════════════════════════════════════════
// G3 Blend with Constraints — 带约束的 G3 过渡
// ═══════════════════════════════════════════════════════════
/// G0/G1/G2/G3 连续性约束边参数
struct G3ConstraintEdges {
EdgeParams g0_edge; ///< G0 位置约束边
EdgeParams g1_edge; ///< G1 切平面约束边
EdgeParams g2_edge; ///< G2 曲率约束边
EdgeParams g3_edge; ///< G3 曲率变化率约束边
bool enforce_g0 = true;
bool enforce_g1 = true;
bool enforce_g2 = true;
bool enforce_g3 = true;
};
/**
* @brief 带约束的 G3 过渡曲面
*
* 在两面之间构造过渡曲面,并精确满足指定的 G0/G1/G2/G3 约束。
* 与 g3_blend 相比,此函数允许用户显式指定每条边的连续性等级。
*
* 算法:
* 1. 根据各约束边采样控制点
* 2. 构造最小二乘系统满足 G0→G3 逐级约束
* 3. 使用 Lagrange 乘子法确保约束精确满足
*
* @param surf_a 曲面 A
* @param surf_b 曲面 B
* @param edges 显式 G0/G1/G2/G3 约束边参数
* @return G3 过渡结果
*
* @ingroup curves
*/
[[nodiscard]] G3BlendResult g3_blend_with_constraints(
const NurbsSurface& surf_a,
const NurbsSurface& surf_b,
const G3ConstraintEdges& edges);
// ═══════════════════════════════════════════════════════════
// Surface Energy Minimization — 薄板能量最小化
// ═══════════════════════════════════════════════════════════
/// 能量最小化选项
struct EnergyMinimizationOptions {
double bending_weight = 1.0; ///< 弯曲能量权重
double membrane_weight = 0.1; ///< 薄膜能量权重
double constraint_weight = 10.0; ///< 约束满足权重
int max_iterations = 100; ///< 最大迭代次数
double tolerance = 1e-6; ///< 收敛容差
double regularization = 1e-4; ///< Tikhonov 正则化
bool preserve_boundary = true; ///< 是否保持边界不变
};
/// 能量最小化结果
struct EnergyMinimizationResult {
NurbsSurface optimized_surface; ///< 优化后的曲面
double initial_bending_energy = 0.0; ///< 初始弯曲能量
double final_bending_energy = 0.0; ///< 最终弯曲能量
double initial_membrane_energy = 0.0; ///< 初始薄膜能量
double final_membrane_energy = 0.0; ///< 最终薄膜能量
int iterations = 0; ///< 实际迭代次数
bool converged = false; ///< 是否收敛
std::vector<Point3D> control_displacements; ///< 控制点位移
double max_displacement = 0.0; ///< 最大控制点位移
};
/**
* @brief 薄板能量最小化曲面优化
*
* 最小化薄板弯曲能量(二阶导数平方积分)和薄膜能量(一阶导数平方积分),
* 同时满足约束条件,实现曲面的全局光顺。
*
* 能量泛函:
* E = w_bend * ∫(κ₁² + κ₂²)dA + w_membrane * ∫(‖∂S/∂u‖² + ‖∂S/∂v‖²)dA
* + w_constraint * Σ‖S(u_i, v_i) - target_i‖²
*
* 算法:基于控制点的 Gauss-Newton 非线性最小二乘优化。
*
* @param surface 输入 NURBS 曲面
* @param constraints 约束点列表(可为空,仅做光顺)
* @param options 优化选项
* @return 能量最小化结果
*
* @note 对标 ICEM Surf / Alias AutoStudio 的 Surface Fairing
* @ingroup curves
*/
[[nodiscard]] EnergyMinimizationResult surface_energy_minimization(
const NurbsSurface& surface,
const std::vector<ShapeConstraint>& constraints,
const EnergyMinimizationOptions& options = {});
// ═══════════════════════════════════════════════════════════
// Curvature Continuity Optimization — 曲率连续性迭代精化
// ═══════════════════════════════════════════════════════════
/// 曲率连续性优化选项
struct CurvatureContinuityOptions {
double position_tolerance = 1e-7; ///< 位置连续性容差
double tangent_tolerance = 1e-5; ///< 切平面角度容差 (rad)
double curvature_tolerance = 1e-4; ///< 曲率偏差容差
int max_iterations = 50; ///< 最大迭代次数
int boundary_samples = 30; ///< 边界采样点数
double step_size = 0.05; ///< 步长因子
bool optimize_both_surfaces = false; ///< 是否同时优化两面
};
/// 曲率连续性优化结果
struct CurvatureContinuityResult {
NurbsSurface optimized_a; ///< 优化后的曲面 A(若 optimize_both_surfaces=true
NurbsSurface optimized_b; ///< 优化后的曲面 B
double initial_g0_error = 0.0; ///< 初始 G0 误差
double final_g0_error = 0.0; ///< 最终 G0 误差
double initial_g1_error = 0.0; ///< 初始 G1 误差 (rad)
double final_g1_error = 0.0; ///< 最终 G1 误差 (rad)
double initial_g2_error = 0.0; ///< 初始 G2 误差
double final_g2_error = 0.0; ///< 最终 G2 误差
int iterations = 0; ///< 实际迭代次数
bool converged = false; ///< 是否收敛
};
/**
* @brief 曲率连续性迭代精化优化
*
* 在两曲面公共边界处迭代调整控制点,逐步提升 G0→G1→G2 连续性。
* 每次迭代沿公共边界采样,计算当前连续性误差,梯度下降调整影响区域控制点。
*
* 算法:
* 1. 检测公共边界
* 2. 沿边界采样,计算各点 G0/G1/G2 误差
* 3. 梯度下降调整边界面附近控制点
* 4. 重新评估,调整步长(adaptive step size
* 5. 收敛检测
*
* @param surf_a 曲面 A
* @param surf_b 曲面 B
* @param options 优化选项
* @return 优化结果(含优化后的曲面和收敛历史)
*
* @note 对标 CATIA GSD Join / ICEM Surf Match
* @ingroup curves
*/
[[nodiscard]] CurvatureContinuityResult curvature_continuity_optimization(
const NurbsSurface& surf_a,
const NurbsSurface& surf_b,
const CurvatureContinuityOptions& options = {});
// ═══════════════════════════════════════════════════════════
// Reflection Line Discontinuity — 反射线不连续检测+修复
// ═══════════════════════════════════════════════════════════
/// 反射线不连续检测参数
struct ReflectionDiscontinuityParams {
Vector3D light_direction; ///< 光源方向(归一化)
int res_u = 40; ///< u 方向分辨率
int res_v = 40; ///< v 方向分辨率
double discontinuity_threshold = 0.05; ///< 不连续判定阈值(方向变化率)
int max_fix_iterations = 20; ///< 最大修复迭代次数
double fix_strength = 0.1; ///< 修复强度
bool auto_fix = true; ///< 是否自动修复
};
/// 反射线不连续检测结果
struct ReflectionDiscontinuityResult {
int res_u = 0, res_v = 0; ///< 网格分辨率
/// 反射场 grid
std::vector<std::vector<Vector3D>> reflection_field;
/// 不连续强度网格 ∈ [0, ∞)
std::vector<std::vector<double>> discontinuity_map;
/// 不连续点列表 (u, v, intensity)
std::vector<std::tuple<double, double, double>> discontinuities;
int total_discontinuities = 0; ///< 不连续点总数
double max_discontinuity = 0.0; ///< 最大不连续强度
double mean_discontinuity = 0.0; ///< 平均不连续强度
bool is_smooth = false; ///< 是否判定为光顺
NurbsSurface fixed_surface; ///< 修复后的曲面(若 auto_fix=true
bool fixed = false; ///< 是否已修复
double fix_residual = 0.0; ///< 修复残差
};
/**
* @brief 反射线不连续检测与修复
*
* 计算曲面在给定光源方向下的反射线场,检测反射方向的局部突变点,
* 并可选择性地自动修复(通过局部控制点微调消除不连续)。
*
* 算法:
* - 检测:计算反射方向场的梯度幅值,超过阈值的标记为不连续
* - 修复:对不连续区域的控制点施加局部高斯加权偏移,最小化反射梯度
*
* @param surface NURBS 曲面
* @param params 检测参数(光源方向、分辨率、阈值、修复选项)
* @return 反射线不连续检测与修复结果
*
* @note 对标 CGM Reflection Line Analysis + Repair
* @ingroup curves
*/
[[nodiscard]] ReflectionDiscontinuityResult reflection_line_discontinuity(
const NurbsSurface& surface,
const ReflectionDiscontinuityParams& params);
} // namespace vde::curves
+136 -1
View File
@@ -282,8 +282,143 @@ FEAMesh boundary_layer_mesh(const brep::BrepModel& body,
FEAMesh hexahedral_mesh(const brep::BrepModel& body,
const HexMeshParams& params = {});
// ════════════════════════════════════════════════════════
// 高级六面体网格生成 API
// ════════════════════════════════════════════════════════
/// 六面体质量优化选项
struct HexOptimizationParams {
double min_scaled_jacobian = 0.3; ///< 最小可接受雅可比
double max_aspect_ratio = 10.0; ///< 最大长宽比
int max_iterations = 50; ///< 最大优化迭代次数
double convergence_tol = 1e-4; ///< 收敛容差
bool untangle = true; ///< 是否翻转修复(untangle inverted elements
bool laplacian_smooth = true; ///< 是否 Laplacian 光顺
bool optimization_based_smooth = true; ///< 是否基于优化的光顺
};
/// 六面体质量优化结果
struct HexOptimizationResult {
FEAMesh optimized_mesh; ///< 优化后的网格
FEAQualityReport initial_quality; ///< 初始质量报告
FEAQualityReport final_quality; ///< 最终质量报告
int iterations = 0; ///< 实际迭代次数
int inverted_fixed = 0; ///< 修复的反转单元数
int degenerate_fixed = 0; ///< 修复的退化单元数
bool converged = false; ///< 是否收敛
};
/// 面标识结构
struct FaceRegion {
std::vector<int> face_indices; ///< 面的顶点索引列表
std::string name; ///< 面名称
};
/// 多块分区定义
struct BlockRegion {
std::vector<Point3D> corner_vertices; ///< 8个角顶点(i,j,k 顺序)
int n_i = 2; ///< i方向分段数
int n_j = 2; ///< j方向分段数
int n_k = 2; ///< k方向分段数
};
/**
* @brief 自适应网格细化
* @brief 映射法六面体网格生成
*
* 给定 B-Rep 实体的源面和目标面,使用映射法生成结构化六面体网格。
* 核心思路:将源面四边形网格通过坐标映射变换到目标面,
* 并在中间层线性插值,形成规则的六面体网格。
*
* 算法:
* 1. 在源面生成四边形网格(重心映射或栅格映射)
* 2. 在目标面生成对应的四边形网格
* 3. 沿 sweep 方向线性插值生成中间层
* 4. 连接各层为六面体单元
*
* @param body B-Rep实体模型
* @param source_face 源面标识
* @param target_face 目标面标识
* @param layers 层数(默认 4
* @return FEAMesh 六面体网格 (Hex8)
*
* @note 对标 Ansys ICEM CFD Blocking / Pointwise
* @ingroup mesh
*/
[[nodiscard]] FEAMesh mapped_hex_mesh(
const brep::BrepModel& body,
const FaceRegion& source_face,
const FaceRegion& target_face,
int layers = 4);
/**
* @brief 子映射法六面体网格生成
*
* 自动将复杂几何体分区为多个可映射子区域,
* 然后在每个子区域内用映射法生成六面体网格,
* 最后缝合各子区域网格。
*
* 算法:
* 1. 分析几何体的拓扑特征(角点、棱边)
* 2. 自动分区为可扫掠(sweepable)子区域
* 3. 对每个子区域执行映射法六面体化
* 4. 合并子区域网格,保证界面节点一致
*
* @param body B-Rep实体模型
* @return FEAMesh 六面体网格 (Hex8)
*
* @note 对标 Ansys Workbench MultiZone / CUBIT Submap
* @ingroup mesh
*/
[[nodiscard]] FEAMesh submapped_hex_mesh(
const brep::BrepModel& body);
/**
* @brief 多块结构化六面体网格生成
*
* 对几何体进行显式多块分解,每个块独立生成结构化六面体网格,
* 确保块间界面节点一一对应。
*
* 算法:
* 1. 根据用户定义或自动识别的块分区
* 2. 每个块内使用 transfinite interpolation (TFI) 生成节点
* 3. 块间共享面节点,保证 conformal 连接
* 4. 组装块为整体网格
*
* @param body B-Rep实体模型
* @param blocks 块分区定义列表
* @return FEAMesh 六面体网格 (Hex8)
*
* @note 对标 Ansys ICEM CFD Hexa Blocking / TrueGrid
* @ingroup mesh
*/
[[nodiscard]] FEAMesh multi_block_hex_mesh(
const brep::BrepModel& body,
const std::vector<BlockRegion>& blocks);
/**
* @brief 六面体网格质量优化与翻转修复
*
* 对六面体网格进行质量优化,包括反转变形修复(untangling)、
* Laplacian 光顺和基于优化的光顺。
*
* 算法:
* - untangling: 检测负雅可比单元,通过解局部优化问题翻转节点
* - Laplacian 光顺: 将内部节点移到邻域质心
* - 优化光顺: 最小化目标函数(扭曲度+体积变化)
*
* @param mesh 输入 FEA 六面体网格
* @param params 优化参数
* @return HexOptimizationResult 优化结果(含优化前后质量报告)
*
* @note 对标 CUBIT MeshGems / Pointwise T-Rex Quality
* @ingroup mesh
*/
[[nodiscard]] HexOptimizationResult hex_quality_optimization(
const FEAMesh& mesh,
const HexOptimizationParams& params = {});
// ════════════════════════════════════════════════════════
// 自适应细化
*
*
* Tet44×Tet4子单元