Browse Source

feat: automatically calculate the parameters for inserting arcs

main
linchforever 10 hours ago
parent
commit
a93a179b2c
  1. 16
      primitive_process/interface/subface/geometry/polyline_fillet.hpp
  2. 5
      primitive_process/src/subface/geometry/extrude_helixline_geometry.cpp
  3. 20
      primitive_process/src/subface/geometry/extrude_polyline_geometry.cpp
  4. 245
      primitive_process/src/subface/geometry/polyline_fillet.cpp

16
primitive_process/interface/subface/geometry/polyline_fillet.hpp

@ -6,14 +6,20 @@ namespace internal
{ {
struct fillet_config_t { struct fillet_config_t {
double segment_ratio = 0.05 ;// 圆角切除量占相邻两段中较短段弦长的比例 bool auto_mode = true;
double min_turn_angle_deg = 0.5; // 低于此角度(度)的衔接处视为共线,跳过 double max_segment_fraction = 0.10; // 每处圆角弧允许裁切相邻线段的最大比例
bool is_axis = false; bool is_axis = false;
double max_fillet_turn_angle_deg = 135.0; // 超过此转角不做圆角
double min_fillet_radius = 1e-3; // 圆角半径下限 double min_turn_angle_deg = 0.5; // 小于此角度视为共线,跳过
double max_fillet_turn_angle_deg = 179.0; // 大于此角度视为极端折叠,跳过
double segment_ratio = 0.05; // 相对比例模式:trim = ratio × min_chord
double min_fillet_radius = 1e-3; // 圆弧半径下限(用于过滤退化圆弧)
double absolute_fillet_radius = 0.0; // 绝对半径模式:> 0 时以此为目标半径
}; };
// 对 2D profile / axis 多段线的衔接顶点插入微小圆角弧,实现 G1 连续。 // 对 2D profile / axis 多段线的衔接顶点插入微小圆角弧
void insert_polyline_corner_fillets(const polyline_descriptor_t& profile, void insert_polyline_corner_fillets(const polyline_descriptor_t& profile,
std::vector<vector3d>& out_points, std::vector<vector3d>& out_points,
std::vector<double>& out_bulges, std::vector<double>& out_bulges,

5
primitive_process/src/subface/geometry/extrude_helixline_geometry.cpp

@ -15,9 +15,8 @@ void extrude_helixline_geometry_t::build()
// 对 profile 各衔接顶点插入微小圆角弧 // 对 profile 各衔接顶点插入微小圆角弧
std::vector<vector3d> filleted_pts; std::vector<vector3d> filleted_pts;
std::vector<double> filleted_bgs; std::vector<double> filleted_bgs;
const fillet_config_t fillet_cfg{/*segment_ratio=*/0.05, fillet_config_t fillet_cfg{};
/*min_turn_angle_deg=*/0.5, fillet_cfg.max_segment_fraction = 0.3;
/*is_axis=*/true};
insert_polyline_corner_fillets(profile_desc, filleted_pts, filleted_bgs, fillet_cfg); insert_polyline_corner_fillets(profile_desc, filleted_pts, filleted_bgs, fillet_cfg);
polyline_descriptor_t filleted_desc{}; polyline_descriptor_t filleted_desc{};

20
primitive_process/src/subface/geometry/extrude_polyline_geometry.cpp

@ -9,12 +9,10 @@ void extrude_polyline_geometry_t::build()
const auto& profile_desc = this->descriptor.profiles[0]; const auto& profile_desc = this->descriptor.profiles[0];
const auto& axis_desc = this->descriptor.axis; const auto& axis_desc = this->descriptor.axis;
fillet_config_t axis_fillet_cfg{};
axis_fillet_cfg.is_axis = true;
std::vector<vector3d> filleted_axis_pts; std::vector<vector3d> filleted_axis_pts;
std::vector<double> filleted_axis_bgs; std::vector<double> filleted_axis_bgs;
const fillet_config_t axis_fillet_cfg{
/*segment_ratio=*/0.01,
/*min_turn_angle_deg=*/0.5,
/*is_axis=*/true};
insert_polyline_corner_fillets(axis_desc, filleted_axis_pts, filleted_axis_bgs, axis_fillet_cfg); insert_polyline_corner_fillets(axis_desc, filleted_axis_pts, filleted_axis_bgs, axis_fillet_cfg);
polyline_descriptor_t filleted_axis_desc{}; polyline_descriptor_t filleted_axis_desc{};
@ -25,16 +23,11 @@ void extrude_polyline_geometry_t::build()
filleted_axis_desc.reference_normal = axis_desc.reference_normal; filleted_axis_desc.reference_normal = axis_desc.reference_normal;
filleted_axis_desc.is_close = axis_desc.is_close; filleted_axis_desc.is_close = axis_desc.is_close;
std::cout << "[extrude_polyline_geometry_t::build] "
<< "axis: " << axis_desc.point_number << " pts → " << filleted_axis_desc.point_number << " pts (after fillet)\n";
axis_geom.build_as_axis(filleted_axis_desc, profile_desc.reference_normal, axis_to_world, polyline_aabb); axis_geom.build_as_axis(filleted_axis_desc, profile_desc.reference_normal, axis_to_world, polyline_aabb);
fillet_config_t profile_fillet_cfg{};
std::vector<vector3d> filleted_profile_pts; std::vector<vector3d> filleted_profile_pts;
std::vector<double> filleted_profile_bgs; std::vector<double> filleted_profile_bgs;
const fillet_config_t profile_fillet_cfg{/*segment_ratio=*/0.05,
/*min_turn_angle_deg=*/0.5,
/*is_axis=*/false};
insert_polyline_corner_fillets(profile_desc, filleted_profile_pts, filleted_profile_bgs, profile_fillet_cfg); insert_polyline_corner_fillets(profile_desc, filleted_profile_pts, filleted_profile_bgs, profile_fillet_cfg);
polyline_descriptor_t filleted_profile_desc{}; polyline_descriptor_t filleted_profile_desc{};
@ -45,9 +38,10 @@ void extrude_polyline_geometry_t::build()
filleted_profile_desc.reference_normal = profile_desc.reference_normal; filleted_profile_desc.reference_normal = profile_desc.reference_normal;
filleted_profile_desc.is_close = profile_desc.is_close; filleted_profile_desc.is_close = profile_desc.is_close;
std::cout << "[extrude_polyline_geometry_t::build] " sanitize_negative_bulge_self_intersections(filleted_profile_pts,
<< "profile: " << profile_desc.point_number << " pts → " << filleted_profile_desc.point_number filleted_profile_bgs,
<< " pts (after fillet)\n"; /*is_axis=*/false,
filleted_profile_desc.is_close);
// 从 axis_to_world 中提取投影方向 // 从 axis_to_world 中提取投影方向
const Eigen::Vector3d proj_x = axis_to_world.matrix().col(0).head<3>(); // Binormal const Eigen::Vector3d proj_x = axis_to_world.matrix().col(0).head<3>(); // Binormal

245
primitive_process/src/subface/geometry/polyline_fillet.cpp

@ -46,12 +46,51 @@ struct Corner {
double fillet_bulge = 0; // 圆弧的 bulge double fillet_bulge = 0; // 圆弧的 bulge
}; };
// 计算弧段 A→B(bulge b)的真实弧长,对直线段直接返回弦长。
static double seg_arc_len(const Eigen::Vector2d& A, const Eigen::Vector2d& B, double bulge)
{
const double chord = (B - A).norm();
if (std::abs(bulge) < 1e-10) return chord;
// 弧段包含角 theta_arc = 4·atan(|b|);弦与半径关系:chord = 2R·sin(theta_arc/2)
const double theta_arc = 4.0 * std::atan(std::abs(bulge));
// 避免 sin(theta_arc/2) → 0 的数值退化
const double sin_half = std::sin(theta_arc * 0.5);
if (sin_half < 1e-15) return chord;
return chord * theta_arc / (2.0 * sin_half);
}
static constexpr double kFilletDensity = 0.06;
static double compute_per_corner_min_trim(double turn_angle,
const Eigen::Vector2d& prev_pt,
const Eigen::Vector2d& cur_pt,
const Eigen::Vector2d& next_pt,
double bulge_in,
double bulge_out,
double max_fraction)
{
const double arc_len_in = seg_arc_len(prev_pt, cur_pt, bulge_in);
const double arc_len_out = seg_arc_len(cur_pt, next_pt, bulge_out);
const double min_len = std::min(arc_len_in, arc_len_out);
// 曲率等化公式
const double cos_q = std::cos(turn_angle / 4.0); // cos(θ/4)
const double R_corner = 2.0 * kFilletDensity * cos_q * cos_q * min_len;
const double trim_nat = R_corner * std::tan(turn_angle / 2.0);
// 上限:trim 是直线裁切量,需与弦长比较
const double chord_in = (cur_pt - prev_pt).norm();
const double chord_out = (next_pt - cur_pt).norm();
return std::min({trim_nat, chord_in * max_fraction, chord_out * max_fraction});
}
// 计算顶点 i 的圆角参数 // 计算顶点 i 的圆角参数
// @param pts 所有顶点 // @param pts 所有顶点(2D)
// @param bulges 所有段的 bulge(bulges[i] 对应段 i→i+1,bulges[prev] 对应入射段) // @param bulges 所有段的 bulge(bulges[i] 对应段 i→i+1,bulges[prev] 对应入射段)
// @param i 当前顶点索引 // @param i 当前顶点索引
// @param prev 入射段起点索引 // @param prev 入射段起点索引
// @param next 出射段终点索引 // @param next 出射段终点索引
// @param cfg 圆角配置
Corner compute_corner(const std::vector<Eigen::Vector2d>& pts, Corner compute_corner(const std::vector<Eigen::Vector2d>& pts,
const std::vector<double>& bulges, const std::vector<double>& bulges,
uint32_t i, uint32_t i,
@ -66,33 +105,52 @@ Corner compute_corner(const std::vector<Eigen::Vector2d>& pts,
const double cos_a = std::clamp(T_in.dot(T_out), -1.0, 1.0); const double cos_a = std::clamp(T_in.dot(T_out), -1.0, 1.0);
const double turn_angle = std::acos(cos_a); const double turn_angle = std::acos(cos_a);
const double half_turn = turn_angle / 2.0;
// 共线判断:转角过小,无需插入圆弧
const double min_turn = cfg.min_turn_angle_deg * (pi / 180.0); const double min_turn = cfg.min_turn_angle_deg * (pi / 180.0);
if (turn_angle < min_turn) return c; // 视为共线,不处理 if (turn_angle < min_turn) return c;
// 转角接近 180° 时,fillet 弧两端点几乎重合,弧半径趋近于零。 // 接近 180° 的折叠,圆弧几何退化,跳过
// 此时 fillet 无法在当前网格分辨率下被正确表达,应跳过以避免几何崩溃。 const double max_turn = cfg.max_fillet_turn_angle_deg * (pi / 180.0);
const double max_fillet_turn_angle_deg = cfg.max_fillet_turn_angle_deg; if (turn_angle > max_turn) return c;
if (turn_angle > max_fillet_turn_angle_deg * (pi / 180.0)) {
std::cout << "转角 " << turn_angle * 180.0 / pi << "° 超过阈值,跳过圆角(避免退化弧)" << std::endl; double trim = 0.0;
return c; if (cfg.auto_mode) {
} // 依据局部转角和相邻弧段长度计算最小圆弧,
trim = compute_per_corner_min_trim(turn_angle,
pts[prev],
pts[i],
pts[next],
bulges[prev],
bulges[i],
cfg.max_segment_fraction);
if (trim < 1e-15) return c; // 近似共线,跳过
} else if (cfg.absolute_fillet_radius > 0.0) {
// 绝对半径
const double chord_in = (pts[i] - pts[prev]).norm(); const double chord_in = (pts[i] - pts[prev]).norm();
const double chord_out = (pts[next] - pts[i]).norm(); const double chord_out = (pts[next] - pts[i]).norm();
const double trim = std::min({std::min(chord_in, chord_out) * cfg.segment_ratio, chord_in * 0.45, chord_out * 0.45}); const double tan_half = std::tan(half_turn);
trim = cfg.absolute_fillet_radius * tan_half;
trim = std::min({trim, chord_in * 0.1, chord_out * 0.1});
if (trim < 1e-15) return c;
// 计算实际 fillet 圆弧半径并与最小可分辨阈值比较 } else {
// R_fillet = trim / tan(turn_angle/2) // 相对比例
const double half_turn = turn_angle / 2.0; const double chord_in = (pts[i] - pts[prev]).norm();
const double R_fillet = (std::tan(half_turn) > 1e-9) ? trim / std::tan(half_turn) : 0.0; const double chord_out = (pts[next] - pts[i]).norm();
if (R_fillet < cfg.min_fillet_radius) { trim = std::min({std::min(chord_in, chord_out) * cfg.segment_ratio, chord_in * 0.1, chord_out * 0.1});
std::cout << "圆角半径 " << R_fillet << " 低于最小阈值 " << cfg.min_fillet_radius << ",跳过圆角" << std::endl; if (trim < 1e-15) return c;
return c;
} }
if (trim < 1e-12) return c; // 额外检查半径下限
if (!cfg.auto_mode) {
const double R_fillet = (std::tan(half_turn) > 1e-15) ? trim / std::tan(half_turn) : 0.0;
if (R_fillet < cfg.min_fillet_radius) return c;
}
// 计算圆弧起终点及 bulge
c.trim_in = pts[i] - trim * T_in; c.trim_in = pts[i] - trim * T_in;
c.trim_out = pts[i] + trim * T_out; c.trim_out = pts[i] + trim * T_out;
@ -108,9 +166,6 @@ Corner compute_corner(const std::vector<Eigen::Vector2d>& pts,
/** /**
* @brief ABbulge b A_newB_new bulge * @brief ABbulge b A_newB_new bulge
* *
* Bulge
* 线b0 0
*
* @param A * @param A
* @param B * @param B
* @param b bulge * @param b bulge
@ -124,46 +179,46 @@ static double recompute_subarc_bulge(const Eigen::Vector2d& A,
const Eigen::Vector2d& A_new, const Eigen::Vector2d& A_new,
const Eigen::Vector2d& B_new) const Eigen::Vector2d& B_new)
{ {
// 直线段:裁切后仍是直线
if (std::abs(b) < 1e-10) return 0.0; if (std::abs(b) < 1e-10) return 0.0;
// 原弧包含角 θ = 4·atan(|b|),半角 = 2·atan(|b|)
const double half_theta = 2.0 * std::atan(std::abs(b)); const double half_theta = 2.0 * std::atan(std::abs(b));
const double sign_b = (b > 0.0) ? 1.0 : -1.0; const double sign_b = (b > 0.0) ? 1.0 : -1.0;
// 原弧弦
const Eigen::Vector2d chord = B - A; const Eigen::Vector2d chord = B - A;
const double chord_len = chord.norm(); const double chord_len = chord.norm();
if (chord_len < 1e-12) return b; if (chord_len < 1e-12) return b;
// 圆半径与圆心
const double R = chord_len / (2.0 * std::sin(half_theta)); const double R = chord_len / (2.0 * std::sin(half_theta));
// 弦中垂线方向(左手正交),对 CCW 弧(b>0)圆心在弦左侧
const Eigen::Vector2d perp_unit(-chord.y() / chord_len, chord.x() / chord_len); const Eigen::Vector2d perp_unit(-chord.y() / chord_len, chord.x() / chord_len);
const double d = R * std::cos(half_theta); // 弦中点到圆心的距离 const double d = R * std::cos(half_theta);
const Eigen::Vector2d center = 0.5 * (A + B) + sign_b * d * perp_unit; const Eigen::Vector2d center = 0.5 * (A + B) + sign_b * d * perp_unit;
// 新起终点在圆上的角度 // 用向量叉积/点积计算子弧张角
const double ang_A = std::atan2(A_new.y() - center.y(), A_new.x() - center.x()); const Eigen::Vector2d rA = A_new - center;
const double ang_B = std::atan2(B_new.y() - center.y(), B_new.x() - center.x()); const Eigen::Vector2d rB = B_new - center;
const double ang_A = std::atan2(rA.y(), rA.x());
const double ang_B = std::atan2(rB.y(), rB.x());
double delta = ang_B - ang_A; double delta = ang_B - ang_A;
// 规范化:子弧行进方向与原弧一致(CCW 为正,CW 为负) // 确保 delta 符号与 b 一致,且 |delta| <= 2π
if (sign_b > 0.0) { if (sign_b > 0.0) {
// 强制 delta 落入 (0, 2π] // CCW:delta 应落入 (0, 2π]
delta = std::fmod(delta, 2.0 * pi); while (delta <= 0.0) delta += 2.0 * pi;
if (delta <= 0.0) delta += 2.0 * pi; while (delta > 2.0 * pi) delta -= 2.0 * pi;
} else { } else {
// 强制 delta 落入 [-2π, 0) // CW:delta 应落入 [-2π, 0)
delta = std::fmod(delta, 2.0 * pi); while (delta >= 0.0) delta -= 2.0 * pi;
if (delta >= 0.0) delta -= 2.0 * pi; while (delta < -2.0 * pi) delta += 2.0 * pi;
} }
double result = std::tan(delta / 4.0); if (std::abs(delta) < 1e-13) {
if ((b > 0) != (result > 0) && std::abs(result) > 1e-9) { const double rA_len = rA.norm();
std::cerr << "[BULGE_SIGN_FLIP] original_b=" << b << " recomputed_bulge=" << result << " delta=" << delta const double rB_len = rB.norm();
<< " ang_A=" << ang_A << " ang_B=" << ang_B << "\n"; if (rA_len < 1e-15 || rB_len < 1e-15) return 0.0;
const double full_theta = 4.0 * std::atan(std::abs(b));
const double ratio = std::abs(delta) / full_theta;
return sign_b * std::abs(b) * ratio;
} }
return std::tan(delta / 4.0); return std::tan(delta / 4.0);
@ -181,8 +236,6 @@ inline void push_point(std::vector<vector3d>& out_points,
/** /**
* @brief p 线 [a, b] 2D * @brief p 线 [a, b] 2D
* @details t [0,1] 线线
* 线线
*/ */
static double point_to_segment_dist_2d(const Eigen::Vector2d& p, const Eigen::Vector2d& a, const Eigen::Vector2d& b) static double point_to_segment_dist_2d(const Eigen::Vector2d& p, const Eigen::Vector2d& a, const Eigen::Vector2d& b)
{ {
@ -193,6 +246,57 @@ static double point_to_segment_dist_2d(const Eigen::Vector2d& p, const Eigen::Ve
return (p - (a + t * ab)).norm(); return (p - (a + t * ab)).norm();
} }
// 计算点 p 到圆弧段(起点A, 终点B, bulge b)的最短距离
static double point_to_arc_dist_2d(const Eigen::Vector2d& p, const Eigen::Vector2d& A, const Eigen::Vector2d& B, double bulge)
{
if (std::abs(bulge) < 1e-9) { return point_to_segment_dist_2d(p, A, B); }
const double half_theta = 2.0 * std::atan(std::abs(bulge));
const double chord_len = (B - A).norm();
if (chord_len < 1e-12) return (p - A).norm();
const double R = chord_len / (2.0 * std::sin(half_theta));
const Eigen::Vector2d chord = B - A;
const double sign_b = (bulge > 0.0) ? 1.0 : -1.0;
const Eigen::Vector2d perp(-chord.y() / chord_len, chord.x() / chord_len);
const double d_center = R * std::cos(half_theta);
const Eigen::Vector2d C = 0.5 * (A + B) + sign_b * d_center * perp;
const Eigen::Vector2d cp = p - C;
const double cp_len = cp.norm();
const double ang_A = std::atan2((A - C).y(), (A - C).x());
const double ang_B = std::atan2((B - C).y(), (B - C).x());
const double ang_p = std::atan2(cp.y(), cp.x());
auto angle_in_arc = [&]() -> bool {
double span = ang_B - ang_A;
if (sign_b > 0.0) {
while (span <= 0.0) span += 2.0 * pi;
while (span > 2.0 * pi) span -= 2.0 * pi;
} else {
while (span >= 0.0) span -= 2.0 * pi;
while (span < -2.0 * pi) span += 2.0 * pi;
}
double rel = ang_p - ang_A;
if (sign_b > 0.0) {
while (rel < 0.0) rel += 2.0 * pi;
while (rel > 2.0 * pi) rel -= 2.0 * pi;
return rel <= std::abs(span);
} else {
while (rel > 0.0) rel -= 2.0 * pi;
while (rel < -2.0 * pi) rel += 2.0 * pi;
return rel >= span;
}
};
if (cp_len > 1e-15 && angle_in_arc()) {
return std::abs(cp_len - R);
} else {
return std::min((p - A).norm(), (p - B).norm());
}
}
} // anonymous namespace } // anonymous namespace
/** /**
@ -200,18 +304,10 @@ static double point_to_segment_dist_2d(const Eigen::Vector2d& p, const Eigen::Ve
* *
* @details * @details
* bulgeCW CCW profile 线 * bulgeCW CCW profile 线
* < RPMC * < R bulge
* SDF * 0退线
*
* bulge 0退线
*
* CW
* AB half_theta = 2*atan(|b|)
* R = |AB| / (2 * sin(half_theta))
* C = midpoint(A,B) + R*cos(half_theta) * perp_right(AB)
* dist(C, ) < R
* *
* @param pts_3d polyline 3D * @param pts_3d polyline
* @param bulges bulge 0 * @param bulges bulge 0
* @param is_axis true = XZ Axisfalse = XY Profile * @param is_axis true = XZ Axisfalse = XY Profile
* @param closed 线 * @param closed 线
@ -245,42 +341,31 @@ void sanitize_negative_bulge_self_intersections(const std::vector<vector3d>& pts
const double chord_len = (B - A).norm(); const double chord_len = (B - A).norm();
if (chord_len < 1e-12) continue; if (chord_len < 1e-12) continue;
// 计算 CW 弧的圆心与半径
// half_theta = theta/2,其中 theta = 4*atan(|b|) 为弧所对圆心角
const double abs_b = std::abs(b); const double abs_b = std::abs(b);
const double half_theta = 2.0 * std::atan(abs_b); const double half_theta = 2.0 * std::atan(abs_b);
const double R = chord_len / (2.0 * std::sin(half_theta)); const double R = chord_len / (2.0 * std::sin(half_theta));
const Eigen::Vector2d chord_dir = (B - A) / chord_len; const Eigen::Vector2d chord_dir = (B - A) / chord_len;
// CW 弧(b < 0)的圆心在弦 AB 的右侧
const Eigen::Vector2d perp_right = {chord_dir.y(), -chord_dir.x()}; const Eigen::Vector2d perp_right = {chord_dir.y(), -chord_dir.x()};
const double d_center = R * std::cos(half_theta); // 弦中点到圆心的距离 const double d_center = R * std::cos(half_theta);
const Eigen::Vector2d center = 0.5 * (A + B) + d_center * perp_right; const Eigen::Vector2d center = 0.5 * (A + B) + d_center * perp_right;
bool self_intersects = false; bool self_intersects = false;
// ---- 检测与前一段 [pts[i-1], A] 的自相交 ---- // 检测与前一段 [pts[i-1], A] 的自相交
if (closed || i > 0u) { if (closed || i > 0u) {
const uint32_t i0 = (i + n - 1u) % n; const uint32_t i0 = (i + n - 1u) % n;
const double dist = point_to_segment_dist_2d(center, pts[i0], A); const double dist = point_to_segment_dist_2d(center, pts[i0], A);
if (dist < R * (1.0 - 1e-6)) { if (dist < R * (1.0 - 1e-6)) {
self_intersects = true; self_intersects = true;
std::cout << "[SANITIZE_ARC] 段[" << i << "] 负bulge圆弧"
<< "(R=" << R << ", bulge=" << b << ""
<< "圆心到前段距离=" << dist << " < R,"
<< "检测到与前段自相交,bulge → 0(退化直线)\n";
} }
} }
// ---- 检测与后一段 [B, pts[i+2]] 的自相交 ---- // 检测与后一段 [B, pts[i+2]] 的自相交
if (!self_intersects && (closed || i + 2u < n)) { if (!self_intersects && (closed || i + 2u < n)) {
const uint32_t i2 = (i + 2u) % n; const uint32_t i2 = (i + 2u) % n;
const double dist = point_to_segment_dist_2d(center, B, pts[i2]); const double dist = point_to_segment_dist_2d(center, B, pts[i2]);
if (dist < R * (1.0 - 1e-6)) { if (dist < R * (1.0 - 1e-6)) {
self_intersects = true; self_intersects = true;
std::cout << "[SANITIZE_ARC] 段[" << i << "] 负bulge圆弧"
<< "(R=" << R << ", bulge=" << b << ""
<< "圆心到后段距离=" << dist << " < R,"
<< "检测到与后段自相交,bulge → 0(退化直线)\n";
} }
} }
@ -304,17 +389,15 @@ void insert_polyline_corner_fillets(const polyline_descriptor_t& desc,
if (cfg.is_axis) { if (cfg.is_axis) {
// axis:XZ 平面 // axis:XZ 平面
to_2d = [](const vector3d& p) -> Eigen::Vector2d { return {p.z, p.x}; }; // 3D -> 2D: (Z, X) to_2d = [](const vector3d& p) -> Eigen::Vector2d { return {p.z, p.x}; };
to_3d = [](const Eigen::Vector2d& q, double fixed_y) -> vector3d { to_3d = [](const Eigen::Vector2d& q, double fixed_y) -> vector3d { return {q.y(), fixed_y, q.x()}; };
return {q.y(), fixed_y, q.x()};
}; // 2D -> 3D: (X, Y, Z) = (q.y, fixed_y, q.x)
} else { } else {
// profile:XY 平面 // profile:XY 平面
to_2d = [](const vector3d& p) -> Eigen::Vector2d { return {p.x, p.y}; }; to_2d = [](const vector3d& p) -> Eigen::Vector2d { return {p.x, p.y}; };
to_3d = [](const Eigen::Vector2d& q, double fixed_z) -> vector3d { return {q.x(), q.y(), fixed_z}; }; to_3d = [](const Eigen::Vector2d& q, double fixed_z) -> vector3d { return {q.x(), q.y(), fixed_z}; };
} }
// 退化情况 // 退化情况:顶点数 < 3,无法形成转角
if (n < 3) { if (n < 3) {
for (uint32_t i = 0; i < n; ++i) { for (uint32_t i = 0; i < n; ++i) {
out_points.push_back(desc.points[i]); out_points.push_back(desc.points[i]);
@ -325,21 +408,20 @@ void insert_polyline_corner_fillets(const polyline_descriptor_t& desc,
std::vector<Eigen::Vector2d> pts(n); std::vector<Eigen::Vector2d> pts(n);
std::vector<double> bulges(n); std::vector<double> bulges(n);
// fixed_coord:axis→y分量(恒=0),profile→z分量(恒=0)
std::vector<double> fixed_coords(n); std::vector<double> fixed_coords(n);
for (uint32_t i = 0; i < n; ++i) { for (uint32_t i = 0; i < n; ++i) {
pts[i] = to_2d(desc.points[i]); pts[i] = to_2d(desc.points[i]);
bulges[i] = desc.bulges ? desc.bulges[i] : 0.0; bulges[i] = desc.bulges ? desc.bulges[i] : 0.0;
fixed_coords[i] = cfg.is_axis ? desc.points[i].y // axis:保留 y(=0) fixed_coords[i] = cfg.is_axis ? desc.points[i].y : desc.points[i].z;
: desc.points[i].z; // profile:保留 z(=0)
} }
// 计算每个顶点的圆角 // 计算每个顶点的圆角参数
std::vector<Corner> corners(n); std::vector<Corner> corners(n);
if (closed) { if (closed) {
for (uint32_t i = 0; i < n; ++i) corners[i] = compute_corner(pts, bulges, i, (i + n - 1) % n, (i + 1) % n, cfg); for (uint32_t i = 0; i < n; ++i) corners[i] = compute_corner(pts, bulges, i, (i + n - 1) % n, (i + 1) % n, cfg);
} else { } else {
// 开放曲线:首尾两端无转角
for (uint32_t i = 1; i <= n - 2; ++i) corners[i] = compute_corner(pts, bulges, i, i - 1, i + 1, cfg); for (uint32_t i = 1; i <= n - 2; ++i) corners[i] = compute_corner(pts, bulges, i, i - 1, i + 1, cfg);
} }
@ -357,10 +439,10 @@ void insert_polyline_corner_fillets(const polyline_descriptor_t& desc,
const Eigen::Vector2d seg_start = corners[i].active ? corners[i].trim_out : pts[i]; const Eigen::Vector2d seg_start = corners[i].active ? corners[i].trim_out : pts[i];
const Eigen::Vector2d seg_end = corners[next].active ? corners[next].trim_in : pts[next]; const Eigen::Vector2d seg_end = corners[next].active ? corners[next].trim_in : pts[next];
// 子弧端点已改变,必须重算 bulge // 重算 bulge
const double seg_bulge = recompute_subarc_bulge(pts[i], pts[next], bulges[i], seg_start, seg_end); const double seg_bulge = recompute_subarc_bulge(pts[i], pts[next], bulges[i], seg_start, seg_end);
push_pt(seg_start, fixed_coords[i], seg_bulge); // 原来是 bulges[i] push_pt(seg_start, fixed_coords[i], seg_bulge);
if (corners[next].active) push_pt(corners[next].trim_in, fixed_coords[next], corners[next].fillet_bulge); if (corners[next].active) push_pt(corners[next].trim_in, fixed_coords[next], corners[next].fillet_bulge);
} }
} else { } else {
@ -370,7 +452,6 @@ void insert_polyline_corner_fillets(const polyline_descriptor_t& desc,
const Eigen::Vector2d seg_start = (i == 0) ? pts[0] : (corners[i].active ? corners[i].trim_out : pts[i]); const Eigen::Vector2d seg_start = (i == 0) ? pts[0] : (corners[i].active ? corners[i].trim_out : pts[i]);
const Eigen::Vector2d seg_end = (next < n - 1 && corners[next].active) ? corners[next].trim_in : pts[next]; const Eigen::Vector2d seg_end = (next < n - 1 && corners[next].active) ? corners[next].trim_in : pts[next];
const double seg_bulge = recompute_subarc_bulge(pts[i], pts[next], bulges[i], seg_start, seg_end); const double seg_bulge = recompute_subarc_bulge(pts[i], pts[next], bulges[i], seg_start, seg_end);
push_pt(seg_start, fixed_coords[i], seg_bulge); push_pt(seg_start, fixed_coords[i], seg_bulge);

Loading…
Cancel
Save