Compare commits
5 Commits
c4d9968e9f
...
557f1e49d9
| Author | SHA1 | Date |
|---|---|---|
|
|
557f1e49d9 | 4 months ago |
|
|
16f32e18cc | 4 months ago |
|
|
da3efa945c | 4 months ago |
|
|
57c7a7a865 | 4 months ago |
|
|
717b151b0a | 4 months ago |
7 changed files with 338 additions and 183 deletions
@ -0,0 +1,25 @@ |
|||||
|
#pragma once |
||||
|
#include <vector> |
||||
|
#include <primitive_descriptor.h> |
||||
|
|
||||
|
namespace internal |
||||
|
{ |
||||
|
|
||||
|
struct fillet_config_t { |
||||
|
double segment_ratio = 0.05 ;// 圆角切除量占相邻两段中较短段弦长的比例
|
||||
|
double min_turn_angle_deg = 0.5; // 低于此角度(度)的衔接处视为共线,跳过
|
||||
|
|
||||
|
// 当锐角过尖(turn_angle > max_fillet_angle_deg),
|
||||
|
// 即使达到 min_fillet_radius 也无法避免自相交时的处理策略:
|
||||
|
// true → 跳过该顶点(保留原始尖角,不插入圆弧)
|
||||
|
// false → 用最大允许 trim 插入圆弧(会有轻微自相交,但尽力修复)
|
||||
|
bool is_axis = false; |
||||
|
}; |
||||
|
|
||||
|
// 对 2D profile / axis 多段线的衔接顶点插入微小圆角弧,实现 G1 连续。
|
||||
|
void insert_polyline_corner_fillets(const polyline_descriptor_t& profile, |
||||
|
std::vector<vector3d>& out_points, |
||||
|
std::vector<double>& out_bulges, |
||||
|
const fillet_config_t& cfg = {}); |
||||
|
|
||||
|
} // namespace internal
|
||||
@ -0,0 +1,192 @@ |
|||||
|
#include <subface/geometry/polyline_fillet.hpp> |
||||
|
#include <math/math_defs.hpp> |
||||
|
|
||||
|
namespace internal |
||||
|
{ |
||||
|
namespace |
||||
|
{ |
||||
|
|
||||
|
// 将 2D 向量 v 旋转 angle 弧度(逆时针为正)
|
||||
|
inline Eigen::Vector2d rotate2d(Eigen::Vector2d v, double angle) |
||||
|
{ |
||||
|
const double c = std::cos(angle), s = std::sin(angle); |
||||
|
return {c * v.x() - s * v.y(), s * v.x() + c * v.y()}; |
||||
|
} |
||||
|
|
||||
|
// 圆弧段 A→B(bulge b)在终点 B 处的切线方向
|
||||
|
Eigen::Vector2d arc_end_tangent(Eigen::Vector2d A, Eigen::Vector2d B, double bulge) |
||||
|
{ |
||||
|
Eigen::Vector2d d = B - A; |
||||
|
if (d.norm() < 1e-12) return Eigen::Vector2d::UnitX(); |
||||
|
d.normalize(); |
||||
|
if (std::abs(bulge) < 1e-12) return d; // 直线段
|
||||
|
|
||||
|
const double sign = bulge > 0 ? 1.0 : -1.0; |
||||
|
const double theta = 4.0 * std::atan(std::abs(bulge)); |
||||
|
return rotate2d(d, sign * theta / 2.0); |
||||
|
} |
||||
|
|
||||
|
// 圆弧段 A→B(bulge b)在起点 A 处的切线方向
|
||||
|
Eigen::Vector2d arc_start_tangent(Eigen::Vector2d A, Eigen::Vector2d B, double bulge) |
||||
|
{ |
||||
|
Eigen::Vector2d d = B - A; |
||||
|
if (d.norm() < 1e-12) return Eigen::Vector2d::UnitX(); |
||||
|
d.normalize(); |
||||
|
if (std::abs(bulge) < 1e-12) return d; |
||||
|
|
||||
|
const double sign = bulge > 0 ? 1.0 : -1.0; |
||||
|
const double theta = 4.0 * std::atan(std::abs(bulge)); |
||||
|
return rotate2d(d, -sign * theta / 2.0); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
struct Corner { |
||||
|
bool active = false; |
||||
|
Eigen::Vector2d trim_in; // 圆弧起点
|
||||
|
Eigen::Vector2d trim_out; // 圆弧终点
|
||||
|
double fillet_bulge = 0; // 圆弧的 bulge
|
||||
|
}; |
||||
|
|
||||
|
// 计算顶点 i 的圆角参数
|
||||
|
// @param pts 所有顶点
|
||||
|
// @param bulges 所有段的 bulge(bulges[i] 对应段 i→i+1,bulges[prev] 对应入射段)
|
||||
|
// @param i 当前顶点索引
|
||||
|
// @param prev 入射段起点索引
|
||||
|
// @param next 出射段终点索引
|
||||
|
Corner compute_corner(const std::vector<Eigen::Vector2d>& pts, |
||||
|
const std::vector<double>& bulges, |
||||
|
uint32_t i, |
||||
|
uint32_t prev, |
||||
|
uint32_t next, |
||||
|
const fillet_config_t& cfg) |
||||
|
{ |
||||
|
Corner c{}; |
||||
|
|
||||
|
const Eigen::Vector2d T_in = arc_end_tangent(pts[prev], pts[i], bulges[prev]); |
||||
|
const Eigen::Vector2d T_out = arc_start_tangent(pts[i], pts[next], bulges[i]); |
||||
|
|
||||
|
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 min_turn = cfg.min_turn_angle_deg * (pi / 180.0); |
||||
|
if (turn_angle < min_turn) return c; // 视为共线,不处理
|
||||
|
|
||||
|
// 切除距离:取相邻两段弦长最小值的比例,并硬限幅
|
||||
|
const double chord_in = (pts[i] - pts[prev]).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}); |
||||
|
|
||||
|
if (trim < 1e-12) return c; |
||||
|
|
||||
|
c.trim_in = pts[i] - trim * T_in; |
||||
|
c.trim_out = pts[i] + trim * T_out; |
||||
|
|
||||
|
// bulge 符号:cross>0 → 左转(CCW多边形凸角) → CW弧 → 负 bulge
|
||||
|
double fillet_bulge = std::tan(turn_angle / 4.0); |
||||
|
const double cross = T_in.x() * T_out.y() - T_in.y() * T_out.x(); |
||||
|
if (cross < 0.0) fillet_bulge = -fillet_bulge; |
||||
|
|
||||
|
c.active = true; |
||||
|
c.fillet_bulge = fillet_bulge; |
||||
|
return c; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
inline void push_point(std::vector<vector3d>& out_points, |
||||
|
std::vector<double>& out_bulges, |
||||
|
const Eigen::Vector2d& p, |
||||
|
double z, |
||||
|
double bulge) |
||||
|
{ |
||||
|
out_points.push_back({p.x(), p.y(), z}); |
||||
|
out_bulges.push_back(bulge); |
||||
|
} |
||||
|
|
||||
|
} // anonymous namespace
|
||||
|
|
||||
|
|
||||
|
void insert_polyline_corner_fillets(const polyline_descriptor_t& desc, |
||||
|
std::vector<vector3d>& out_points, |
||||
|
std::vector<double>& out_bulges, |
||||
|
const fillet_config_t& cfg) |
||||
|
{ |
||||
|
out_points.clear(); |
||||
|
out_bulges.clear(); |
||||
|
|
||||
|
const uint32_t n = desc.point_number; |
||||
|
const bool closed = desc.is_close; |
||||
|
|
||||
|
std::function<Eigen::Vector2d(const vector3d&)> to_2d; |
||||
|
std::function<vector3d(const Eigen::Vector2d&, double)> to_3d; |
||||
|
|
||||
|
if (cfg.is_axis) { |
||||
|
// axis:XZ 平面
|
||||
|
to_2d = [](const vector3d& p) -> Eigen::Vector2d { return {p.z, p.x}; }; // 3D -> 2D: (Z, X)
|
||||
|
to_3d = [](const Eigen::Vector2d& q, double fixed_y) -> vector3d {return {q.y(), fixed_y, q.x()};}; // 2D -> 3D: (X, Y, Z) = (q.y, fixed_y, q.x)
|
||||
|
} else { |
||||
|
// profile:XY 平面
|
||||
|
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};}; |
||||
|
} |
||||
|
|
||||
|
// 退化情况
|
||||
|
if (n < 3) { |
||||
|
for (uint32_t i = 0; i < n; ++i) { |
||||
|
out_points.push_back(desc.points[i]); |
||||
|
if (i < n - 1) out_bulges.push_back(desc.bulges ? desc.bulges[i] : 0.0); |
||||
|
} |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
std::vector<Eigen::Vector2d> pts(n); |
||||
|
std::vector<double> bulges(n); |
||||
|
// fixed_coord:axis→y分量(恒=0),profile→z分量(恒=0)
|
||||
|
std::vector<double> fixed_coords(n); |
||||
|
|
||||
|
for (uint32_t i = 0; i < n; ++i) { |
||||
|
pts[i] = to_2d(desc.points[i]); |
||||
|
bulges[i] = desc.bulges ? desc.bulges[i] : 0.0; |
||||
|
fixed_coords[i] = cfg.is_axis ? desc.points[i].y // axis:保留 y(=0)
|
||||
|
: desc.points[i].z; // profile:保留 z(=0)
|
||||
|
} |
||||
|
|
||||
|
// 计算每个顶点的圆角
|
||||
|
std::vector<Corner> corners(n); |
||||
|
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); |
||||
|
} else { |
||||
|
for (uint32_t i = 1; i <= n - 2; ++i) corners[i] = compute_corner(pts, bulges, i, i - 1, i + 1, cfg); |
||||
|
} |
||||
|
|
||||
|
auto push_pt = [&](const Eigen::Vector2d& p2d, double fixed, double bulge) { |
||||
|
out_points.push_back(to_3d(p2d, fixed)); |
||||
|
out_bulges.push_back(bulge); |
||||
|
}; |
||||
|
|
||||
|
out_points.reserve(n * 2); |
||||
|
out_bulges.reserve(n * 2); |
||||
|
|
||||
|
if (closed) { |
||||
|
for (uint32_t i = 0; i < n; ++i) { |
||||
|
const uint32_t next = (i + 1) % n; |
||||
|
const auto& seg_start = corners[i].active ? corners[i].trim_out : pts[i]; |
||||
|
push_pt(seg_start, fixed_coords[i], bulges[i]); |
||||
|
if (corners[next].active) push_pt(corners[next].trim_in, fixed_coords[next], corners[next].fillet_bulge); |
||||
|
} |
||||
|
} else { |
||||
|
for (uint32_t i = 0; i < n - 1; ++i) { |
||||
|
const uint32_t next = i + 1; |
||||
|
const auto& seg_start = (i == 0) ? pts[0] : corners[i].active ? corners[i].trim_out : pts[i]; |
||||
|
push_pt(seg_start, fixed_coords[i], bulges[i]); |
||||
|
if (next < n - 1 && corners[next].active) |
||||
|
push_pt(corners[next].trim_in, fixed_coords[next], corners[next].fillet_bulge); |
||||
|
} |
||||
|
out_points.push_back(desc.points[n - 1]); // 末点原样保留
|
||||
|
} |
||||
|
|
||||
|
if (closed) |
||||
|
assert(out_points.size() == out_bulges.size()); |
||||
|
else |
||||
|
assert(out_bulges.size() + 1 == out_points.size()); |
||||
|
} |
||||
|
} // namespace internal
|
||||
Loading…
Reference in new issue