feat(v12): generative surfaces (Sweep/Loft/Net) + curve tools + surface analysis
CI / Build & Test (push) Failing after 31s
CI / Release Build (push) Failing after 38s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled

M1 — Generative Surfaces:
- sweep_surface: Explicit/Spine/TwoGuides modes
- loft_surface: multi-section interpolation + guide + tangent constraints
- net_surface: bidirectional curve grid → NURBS

M3 — Curve Tools + Analysis:
- curve_tools: project_curve, parallel_curve, connect_curve(G1-G3), helix, isoparametric
- surface_analysis: inflection_lines, checker_mapping, surface_checker_report (A/B/C/D)
- 14/14 tests passed

Fixed: constraint_solver.h duplicate declaration, fea_mesh.h comment syntax

Pending: M2 surface editing (retrying)
This commit is contained in:
茂之钳
2026-07-27 09:08:01 +08:00
parent 989f1f2328
commit bb0029234f
19 changed files with 3623 additions and 5 deletions
+30
View File
@@ -123,4 +123,34 @@ namespace vde::brep {
const BrepModel& body, int face_a, int face_b,
double radius, int samples = 16);
/**
* @brief 形状过渡(Shape Fillet
*
* 沿边采样变半径,逐段建模过渡曲面,自动检测三面交角并
* 以球面填充 + 过渡面处理。
*
* 算法:
* 1. 定位目标边及其两个邻面
* 2. 沿边曲线采样 N 个截面,每截面计算半径(r_start → r_end 线性插值)
* 3. 计算每截面的角平分线方向和切点
* 4. 每相邻两截面间构建一张过渡曲面
* 5. 检测三面交角顶点(目标边端点处 ≥ 3 个面交汇)
* - 若检测到三面交角:在交角顶点处构造球面填充面
* - 球面填充面与相邻过渡面之间插入过渡面
* 6. 重建两个邻面(切除过渡区域),复制其他面,组装壳和体
*
* @param body 输入实体
* @param edge_id 目标边索引
* @param r_start 边起点处的过渡半径(≥ 0)
* @param r_end 边终点处的过渡半径(≥ 0)
* @param samples 沿边的截面采样数(默认 16)
* @return 过渡后的新实体
*
* @pre edge_id 有效,r_start ≥ 0, r_end ≥ 0
* @note 自动检测并处理三面交角,以球面填充 + 过渡面连接
*/
[[nodiscard]] BrepModel shape_fillet(
const BrepModel& body, int edge_id,
double r_start, double r_end, int samples = 16);
} // namespace vde::brep
-4
View File
@@ -584,10 +584,6 @@ public:
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,
+65
View File
@@ -0,0 +1,65 @@
#pragma once
/**
* @file control_point_edit.h
* @brief 曲面编辑 — Control Point Edit(控制点编辑)
*
* 直接修改 NURBS 曲面的指定控制点位置,返回一个新的修改后曲面。
* 保持所有权重大于 0,自动处理退化曲面。
*
* 对标 CATIA / Rhino 的控制点编辑功能。
*
* @ingroup curves
*/
#include "vde/curves/nurbs_surface.h"
#include "vde/core/point.h"
#include <vector>
#include <utility>
namespace vde::curves {
using core::Point3D;
using core::Vector3D;
// ═══════════════════════════════════════════════════════════
// Control Point Edit — 控制点编辑
// ═══════════════════════════════════════════════════════════
/// 控制点编辑结果
struct ControlPointEditResult {
NurbsSurface modified_surface; ///< 修改后的曲面
std::vector<Point3D> displacements; ///< 各控制点的实际位移
double max_displacement = 0.0; ///< 最大控制点位移
int points_modified = 0; ///< 实际修改的控制点数
bool weights_preserved = true; ///< 是否所有权重保持 > 0
};
/**
* @brief 控制点编辑
*
* 修改 NURBS 曲面的指定控制点位置,保持所有权重大于 0,
* 返回一个新的修改后曲面。原始曲面保持不变。
*
* 算法:
* 1. 复制原始曲面的控制点网格和权重网格
* 2. 将指定索引的控制点移动到新位置
* 3. 确保所有权重 >= 1e-12(防止除零)
* 4. 保留原有的节点向量和阶次
* 5. 返回新的 NurbsSurface
*
* @param surface 输入 NURBS 曲面
* @param cp_indices 待修改控制点的 (i, j) 索引列表
* @param new_positions 新位置列表,与 cp_indices 一一对应
* @return 编辑结果
*
* @pre cp_indices 和 new_positions 大小一致
* @pre 所有索引在 [0, nu]×[0, nv] 范围内
* @note 所有权重保持 > 0,若输入权重 ≤ 0 则自动修正为 1e-12
* @ingroup curves
*/
[[nodiscard]] ControlPointEditResult control_point_edit(
const NurbsSurface& surface,
const std::vector<std::pair<int, int>>& cp_indices,
const std::vector<Point3D>& new_positions);
} // namespace vde::curves
+139
View File
@@ -0,0 +1,139 @@
#pragma once
/**
* @file curve_tools.h
* @brief 曲线工具集 — 投影、偏移、连接、螺旋线、等参线
*
* 提供 CAD 级曲线操作功能:
* - project_curve_on_surface: 空间曲线 → 参数空间 p-curve(采样→投影→拟合)
* - parallel_curve: 偏移曲线(等距线)
* - connect_curve: 连接曲线(G1/G2/G3 几何连续性)
* - helix_curve: 螺旋线
* - isoparametric_curves: u/v 等参线提取
*
* project_curve_on_surface 委托给 curve_surface_projection 模块。
*
* @ingroup curves
*/
#include "vde/curves/curve_surface_projection.h"
#include "vde/curves/nurbs_curve.h"
#include "vde/curves/nurbs_surface.h"
#include "vde/core/point.h"
#include <vector>
#include <utility>
namespace vde::curves {
using core::Point3D;
using core::Vector3D;
// project_curve_on_surface is imported from curve_surface_projection.h
// (included above). Users can call it directly:
// curve_tools.h → includes curve_surface_projection.h → project_curve_on_surface available
// ═══════════════════════════════════════════════════════════
// parallel_curve
// ═══════════════════════════════════════════════════════════
/**
* @brief 计算平面曲线的等距偏移(平行曲线)
*
* 将曲线投影到参考平面(默认 XY 平面),沿平面法线方向的曲线法向偏移
* 指定距离。采样原曲线 → 在局部法线方向偏移 → 拟合新 NURBS。
*
* @param curve 原 NURBS 曲线
* @param distance 偏移距离(正值沿法线正方向,负值反向)
* @param plane_normal 参考平面法线(默认 +Z,即曲线在 XY 平面内偏移)
* @param samples 采样点数(默认 100
* @return 偏移后的 NURBS 曲线
*/
[[nodiscard]] NurbsCurve parallel_curve(
const NurbsCurve& curve,
double distance,
const Vector3D& plane_normal = Vector3D(0, 0, 1),
int samples = 100);
// ═══════════════════════════════════════════════════════════
// connect_curve
// ═══════════════════════════════════════════════════════════
/// 几何连续性级别
enum class Continuity {
G1, ///< 切线连续(位置 + 切向匹配)
G2, ///< 曲率连续(位置 + 切向 + 曲率匹配)
G3 ///< 挠率连续(位置 + 切向 + 曲率 + 挠率匹配)
};
/**
* @brief 创建连接两曲线的过渡曲线
*
* 从 c1 的终点连接到 c2 的起点,根据连续性级别匹配端点条件。
* 使用 Bézier 过渡曲线(阶次根据连续性自动选择:G1→3次, G2→5次, G3→7次)。
*
* @param c1 首曲线
* @param c2 尾曲线
* @param continuity 连续性级别
* @param samples 拟合采样点数(默认 50)
* @return 连接过渡 NURBS 曲线
*/
[[nodiscard]] NurbsCurve connect_curve(
const NurbsCurve& c1,
const NurbsCurve& c2,
Continuity continuity,
int samples = 50);
// ═══════════════════════════════════════════════════════════
// helix_curve
// ═══════════════════════════════════════════════════════════
/**
* @brief 创建螺旋线
*
* 以 axis_origin 为起点沿 axis_dir 方向生成螺旋线。
* 参数方程(柱坐标):r = radius, θ = 2π·t·n, z = height·t
* 其中 n = height / pitch 为圈数。
* 采样后拟合为 NURBS。
*
* @param axis_origin 轴线起点
* @param axis_dir 轴线方向(自动归一化)
* @param radius 螺旋半径
* @param pitch 螺距(每圈上升高度),不能为 0
* @param height 总高度(从 axis_origin 沿 axis_dir),> 0
* @param samples 每圈采样点数(默认 50)
* @return 螺旋 NURBS 曲线
*/
[[nodiscard]] NurbsCurve helix_curve(
const Point3D& axis_origin,
const Vector3D& axis_dir,
double radius,
double pitch,
double height,
int samples = 50);
// ═══════════════════════════════════════════════════════════
// isoparametric_curves
// ═══════════════════════════════════════════════════════════
/// 等参线集合
struct IsoparametricCurves {
std::vector<NurbsCurve> u_curves; ///< u = const 等参线(每条对应一个 v 向变化)
std::vector<NurbsCurve> v_curves; ///< v = const 等参线(每条对应一个 u 向变化)
};
/**
* @brief 提取 NURBS 曲面的等参线
*
* 在曲面参数域中均匀分布 u_count 条 u 等参线和 v_count 条 v 等参线。
* 等参线仅为 NURBS 曲面的 1D 截面(一个参数固定、另一参数变化)。
*
* @param surface NURBS 曲面
* @param u_count u 等参线条数(≥ 1
* @param v_count v 等参线条数(≥ 1
* @return 等参线集合
*/
[[nodiscard]] IsoparametricCurves isoparametric_curves(
const NurbsSurface& surface,
int u_count,
int v_count);
} // namespace vde::curves
+135
View File
@@ -0,0 +1,135 @@
#pragma once
#include "vde/core/point.h"
#include "vde/curves/nurbs_surface.h"
#include "vde/curves/nurbs_curve.h"
#include <vector>
#include <optional>
namespace vde::curves {
using core::Point3D;
using core::Vector3D;
// ============================================================================
// Sweep — 扫描曲面
// ============================================================================
/**
* @brief 扫描类型枚举
*/
enum class SweepType : int {
Explicit = 0, ///< 显式扫描:profile 沿 path 平移+旋转(Frenet 标架),无扭转控制
Spine = 1, ///< 脊线扫描:用脊线控制扭转,修正 Frenet 标架
TwoGuides = 2 ///< 双引导线扫描:两条引导线控制 profile 沿路径的缩放
};
/**
* @brief 扫描选项
*
* 控制扫描行为:类型,以及引导线(用于 Spine / TwoGuides)。
* 对于 Explicitguide1/guide2/spine 均忽略;对于 Spine,需提供 spine;对于 TwoGuides,需提供 guide1 和 guide2。
*/
struct SweepOption {
SweepType type = SweepType::Explicit;
/// 脊线(用于 Spine 模式),控制扭转分布
std::optional<NurbsCurve> spine;
/// 第一引导线(用于 TwoGuides 模式)
std::optional<NurbsCurve> guide1;
/// 第二引导线(用于 TwoGuides 模式)
std::optional<NurbsCurve> guide2;
/// 沿路径的采样点数(默认 32)。越大越精确但曲面越重。
int samples = 32;
};
/**
* @brief 扫描曲面
*
* 将 profile 曲线沿 path 曲线扫描,生成 NURBS 曲面。
*
* @param profile 截面曲线(NURBS
* @param path 路径曲线(NURBS
* @param option 扫描选项(类型、引导线、采样数)
* @return 扫描 NURBS 曲面
*
* 三种模式:
* - ExplicitFrenet 标架沿路径对齐,profile 仅平移+旋转
* - Spine:使用独立脊线控制法向,消除 Frenet 标架在曲率零点处的翻转
* - TwoGuidesguide1 和 guide2 与 profile 两端对齐,线性缩放 profile 沿路径变化
*
* @ingroup curves
*/
[[nodiscard]] NurbsSurface sweep_surface(
const NurbsCurve& profile,
const NurbsCurve& path,
const SweepOption& option = {});
// ============================================================================
// Loft — 放样曲面
// ============================================================================
/**
* @brief 放样约束
*
* 控制首尾截面处的切线方向。
*/
struct LoftConstraint {
/// 起始截面切线方向(nullable)。曲面在 v=0 处的 ∂S/∂v 方向。
std::optional<Vector3D> start_tangent;
/// 终止截面切线方向(nullable)。曲面在 v=1 处的 ∂S/∂v 方向。
std::optional<Vector3D> end_tangent;
};
/**
* @brief 放样曲面
*
* 将 N 条截面曲线插值成一张光滑曲面(B 样条插值,v 向)。
*
* @param sections 截面曲线列表(至少 2 条)
* @param guides 引导线(可选)。每条引导线约束一个截面位置的分布;为空则截面均匀分布。
* @param constraints 切线约束(可选)。控制首尾截面处的曲面切线方向。
* @return 放样 NURBS 曲面
*
* 算法步骤:
* 1. 升阶+节点精化使所有截面控制点数一致
* 2. v 向 B 样条插值:每个 u 向控制点列沿 v 向插值
* 3. 若提供引导线,将截面沿引导线方向映射分布
* 4. 若提供切线约束,添加首尾导数条件
*
* @ingroup curves
*/
[[nodiscard]] NurbsSurface loft_surface(
const std::vector<NurbsCurve>& sections,
const std::vector<NurbsCurve>& guides = {},
const LoftConstraint& constraints = {});
// ============================================================================
// Net — 网格曲面
// ============================================================================
/**
* @brief 网格曲面(双向曲线网格 → 张量积 NURBS)
*
* 由 u 向和 v 向两组 NURBS 曲线构成网格曲面。
* 自动对齐交叉点、构建兼容的节点向量,生成张量积 NURBS 表示。
*
* @param u_curves u 向曲线组(≥ 2 条)
* @param v_curves v 向曲线组(≥ 2 条),允许与 u_curves 数量不等
* @return 网格 NURBS 曲面
*
* 算法步骤:
* 1. 计算所有 (u-curve, v-curve) 交叉点参数 (u_i, v_j)
* 2. 按平均参数对齐 u/v 向节点向量
* 3. 构建兼容控制点网格(每个交叉点为控制点)
* 4. 输出张量积 NURBS 曲面
*
* @ingroup curves
*/
[[nodiscard]] NurbsSurface net_surface(
const std::vector<NurbsCurve>& u_curves,
const std::vector<NurbsCurve>& v_curves);
} // namespace vde::curves
+97
View File
@@ -225,4 +225,101 @@ struct DraftAngleDistribution {
const Vector3D& pull_dir,
int samples = 15);
// ═══════════════════════════════════════════════════════════
// Inflection Lines
// ═══════════════════════════════════════════════════════════
/// 曲率过零点线段
struct InflectionSegment {
Point3D start; ///< 线段起点
Point3D end; ///< 线段终点
};
/// 曲率过零分析结果
struct InflectionLinesResult {
std::vector<InflectionSegment> segments; ///< 高斯曲率过零点连线
int res_u, res_v; ///< 采样分辨率
};
/**
* @brief 提取曲面的曲率过零点连线(inflection lines
*
* 在参数域密集采样高斯曲率,检测曲率符号变化点,连接相邻过零点
* 形成线段。用于检测曲面凹凸变化边界。
*
* @param surf NURBS 曲面
* @param res_u u 方向分辨率(默认 50)
* @param res_v v 方向分辨率(默认 50)
* @return 过零点线段集合
*/
[[nodiscard]] InflectionLinesResult inflection_lines(
const NurbsSurface& surf,
int res_u = 50, int res_v = 50);
// ═══════════════════════════════════════════════════════════
// Checker Mapping
// ═══════════════════════════════════════════════════════════
/// 棋盘格反射数据
struct CheckerMapResult {
int res_u, res_v; ///< 分辨率
std::vector<std::vector<double>> intensity; ///< intensity[i][j] ∈ [0,1](棋盘格亮度)
Vector3D view_dir; ///< 视线方向(归一化)
};
/**
* @brief 计算棋盘格反射映射数据
*
* 模拟棋盘格图案在曲面上的反射效果。将参数域 (u,v) 映射为棋盘格
* 纹理坐标,结合曲面法线计算反射强度。用于评估曲面光顺性——
* 反射棋盘格的均匀度和连续性反映曲率连续性。
*
* @param surf NURBS 曲面
* @param view_dir 视线方向(自动归一化)
* @param res_u u 方向分辨率(默认 60)
* @param res_v v 方向分辨率(默认 60)
* @return 棋盘格反射数据
*/
[[nodiscard]] CheckerMapResult checker_mapping(
const NurbsSurface& surf,
const Vector3D& view_dir,
int res_u = 60, int res_v = 60);
// ═══════════════════════════════════════════════════════════
// Surface Checker Report
// ═══════════════════════════════════════════════════════════
/// 光顺性评估等级
enum class SmoothnessGrade {
A, ///< 优秀 — 反射条纹均匀、无突变、曲率连续
B, ///< 良好 — 轻微不均匀、局部小波动
C, ///< 一般 — 明显扭曲、局部不平滑
D ///< 差 — 严重扭曲、曲率突变
};
/// 曲面光顺性评估报告
struct SurfaceCheckerReport {
SmoothnessGrade grade; ///< 光顺性等级
double checker_uniformity; ///< 棋盘格均匀度 [0,1]1 最佳
double curvature_continuity; ///< 曲率连续性指数 [0,1]
double gaussian_range; ///< 高斯曲率范围
double mean_range; ///< 平均曲率范围
int inflection_count; ///< 曲率过零点数量
std::string description; ///< 文字描述
};
/**
* @brief 生成曲面光顺性评估报告
*
* 综合曲率分析、棋盘格反射和过零点检测,对曲面光顺性进行量化评估。
* 返回等级、均匀度、连续性等综合指标。
*
* @param surf NURBS 曲面
* @param res 综合分析分辨率(默认 60)
* @return 光顺性评估报告
*/
[[nodiscard]] SurfaceCheckerReport surface_checker_report(
const NurbsSurface& surf,
int res = 60);
} // namespace vde::curves
+95
View File
@@ -0,0 +1,95 @@
#pragma once
/**
* @file surface_editing.h
* @brief 曲面编辑 — Match Surface(曲面匹配)
*
* 沿共享边界自动调整 target 曲面的控制点,使其与 reference 曲面达到
* G0(位置)、G1(切平面)、G2(曲率)或 G3(曲率变化率)连续性。
*
* 对标 CATIA / NX 的 Match Surface 功能。
*
* @ingroup curves
*/
#include "vde/curves/nurbs_surface.h"
#include "vde/core/point.h"
#include <vector>
namespace vde::curves {
using core::Point3D;
using core::Vector3D;
// ═══════════════════════════════════════════════════════════
// Match Surface — 曲面匹配
// ═══════════════════════════════════════════════════════════
/// 连续性等级
enum class ContinuityLevel {
G0 = 0, // 位置连续(C0
G1 = 1, // 切平面连续(C1
G2 = 2, // 曲率连续(C2
G3 = 3 // 曲率变化率连续(C3
};
/// 边界索引:0=umin, 1=umax, 2=vmin, 3=vmax
enum class BoundaryEdge {
UMin = 0,
UMax = 1,
VMin = 2,
VMax = 3
};
/// 曲面匹配选项
struct MatchSurfaceOptions {
ContinuityLevel continuity = ContinuityLevel::G1; ///< 目标连续性等级
int rows_to_adjust = 0; ///< 待调整控制点排数(0=自动,G0→1, G1→1, G2→2, G3→3
double tolerance = 1e-6; ///< 收敛容差
int boundary_samples = 20; ///< 边界采样点数
bool preserve_weights = true; ///< 是否保持权重不变
bool align_orientation = true; ///< 是否自动对齐曲面朝向
};
/// 曲面匹配结果
struct MatchSurfaceResult {
NurbsSurface matched_surface; ///< 匹配后的 target 曲面
double max_position_error = 0.0; ///< 最大 G0 位置误差
double max_tangent_error = 0.0; ///< 最大 G1 切平面角度误差(弧度)
double max_curvature_error = 0.0; ///< 最大 G2 曲率偏差
double max_derivative_error = 0.0; ///< 最大 G3 曲率变化率偏差
bool g0_ok = false;
bool g1_ok = false;
bool g2_ok = false;
bool g3_ok = false;
int rows_adjusted = 0; ///< 实际调整的控制点排数
std::vector<Point3D> control_displacements; ///< 所有控制点位移
};
/**
* @brief 曲面匹配
*
* 自动调整 target 曲面边界附近的控制点,使其沿 specified edge 与
* reference 曲面达到指定的连续性(G0/G1/G2/G3)。
*
* 算法:
* - G0: 将 target 边界控制点投影到 reference 边界
* - G1: 调整第1排控制点使切平面匹配
* - G2: 调整第1-2排控制点使曲率匹配
* - G3: 调整第1-3排控制点使曲率变化率匹配(最小二乘优化)
*
* @param target 待匹配曲面(边界控制点将被修改)
* @param reference 参考曲面(保持不变)
* @param edge target 曲面要匹配的边界
* @param options 匹配选项
* @return 匹配结果(含修改后曲面和误差统计)
*
* @note G3 需要修改 3 排控制点;G2 修改 2 排;G1/G0 修改 1 排
* @ingroup curves
*/
[[nodiscard]] MatchSurfaceResult match_surface(
const NurbsSurface& target,
const NurbsSurface& reference,
BoundaryEdge edge,
const MatchSurfaceOptions& options = {});
} // namespace vde::curves
+1 -1
View File
@@ -419,7 +419,7 @@ struct BlockRegion {
// ════════════════════════════════════════════════════════
// 自适应细化
*
/**
* 基于误差估计函数对高误差区域进行局部细分。
* 支持边中点分裂细化(适用于Tet4→4×Tet4子单元)。
*