Browse Source

feat: profile_corner_fillets

main
linchforever 4 days ago
parent
commit
717b151b0a
  1. 30
      primitive_process/interface/subface/geometry/polyline_fillet.hpp
  2. 28
      primitive_process/src/subface/geometry/extrude_helixline_geometry.cpp
  3. 28
      primitive_process/src/subface/geometry/extrude_polyline_geometry.cpp
  4. 215
      primitive_process/src/subface/geometry/polyline_fillet.cpp

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

@ -0,0 +1,30 @@
#pragma once
#include <vector>
#include <primitive_descriptor.h>
namespace internal
{
struct fillet_config_t {
// 圆角切除量占相邻两段中较短段弦长的比例(默认 5%)
// 越小则圆角越微小,棱角感越强。
double segment_ratio = 0.05;
// 低于此角度(度)的衔接处视为共线,跳过不处理
double min_turn_angle_deg = 0.5;
};
// 对 2D profile 多段线的衔接顶点插入微小圆角弧,实现 G1 连续。
//
// 闭合多边形:处理全部 n 个顶点,输出隐式首尾相连。
// 不闭合多段线:仅处理内部顶点 [1, n-2],首尾端点原样保留。
//
// @param profile 原始 profile descriptor(通过 is_closed 区分开闭)
// @param out_points 输出点列表
// @param out_bulges 输出 bulge 列表(与 out_points 等长)
// @param cfg 圆角参数
void insert_profile_corner_fillets(const polyline_descriptor_t& profile,
std::vector<vector3d>& out_points,
std::vector<double>& out_bulges,
const fillet_config_t& cfg = {});
} // namespace internal

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

@ -1,4 +1,5 @@
#include <subface/geometry/extrude_helixline_geometry.hpp> #include <subface/geometry/extrude_helixline_geometry.hpp>
#include <subface/geometry/polyline_fillet.hpp>
namespace internal namespace internal
{ {
@ -13,19 +14,40 @@ void extrude_helixline_geometry_t::build()
const auto& matrix_handle = this->axis_to_world.matrix(); const auto& matrix_handle = this->axis_to_world.matrix();
const Eigen::Vector3d N = -matrix_handle.col(0); const Eigen::Vector3d N = -matrix_handle.col(0);
const Eigen::Vector3d T = (matrix_handle.col(2) * this->axis_geom.height // const Eigen::Vector3d T = (matrix_handle.col(2) * this->axis_geom.height
+ this->axis_geom.radius * this->axis_geom.total_theta * matrix_handle.col(1)) + this->axis_geom.radius * this->axis_geom.total_theta * matrix_handle.col(1))
.normalized(); .normalized();
const Eigen::Vector3d B = N.cross(T); const Eigen::Vector3d B = N.cross(T);
// 对 profile 各衔接顶点插入微小圆角弧
std::vector<vector3d> filleted_pts;
std::vector<double> filleted_bgs;
const fillet_config_t fillet_cfg{/*segment_ratio=*/0.02,
/*min_turn_angle_deg=*/0.5};
insert_profile_corner_fillets(profile_desc, filleted_pts, filleted_bgs, fillet_cfg);
polyline_descriptor_t filleted_desc{};
filleted_desc.point_number = static_cast<uint32_t>(filleted_pts.size());
filleted_desc.points = filleted_pts.data();
filleted_desc.bulge_number = static_cast<uint32_t>(filleted_bgs.size());
filleted_desc.bulges = filleted_bgs.data();
filleted_desc.reference_normal = profile_desc.reference_normal;
filleted_desc.is_close = profile_desc.is_close;
std::cout << "[extrude_helixline_geometry_t::build] "
<< "original profile points: " << profile_desc.point_number << ", after fillet: " << filleted_desc.point_number
<< "\n";
// 构建 profile 几何数据(使用插入圆角后的 descriptor)
aabb_t_dim<2> profile_aabb; aabb_t_dim<2> profile_aabb;
profile_geom.build_as_profile(profile_desc, B, N, matrix_handle.col(3).head<3>(), profile_aabb); profile_geom.build_as_profile(filleted_desc, B, N, matrix_handle.col(3).head<3>(), profile_aabb);
std::cout << "[extrude_helixline_geometry_t::build] profile_aabb: min=" << profile_aabb.min().transpose() std::cout << "[extrude_helixline_geometry_t::build] profile_aabb: min=" << profile_aabb.min().transpose()
<< ", max=" << profile_aabb.max().transpose() << std::endl; << ", max=" << profile_aabb.max().transpose() << std::endl;
const double profile_max_extent = std::max(profile_aabb.min().norm(), profile_aabb.max().norm()); const double profile_max_extent = std::max(profile_aabb.min().norm(), profile_aabb.max().norm());
helixline_aabb.min().array() -= profile_max_extent; helixline_aabb.min().array() -= profile_max_extent;
helixline_aabb.max().array() += profile_max_extent; helixline_aabb.max().array() += profile_max_extent;
} }
} // namespace internal } // namespace internal

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

@ -1,4 +1,5 @@
#include <subface/geometry/extrude_polyline_geometry.hpp> #include <subface/geometry/extrude_polyline_geometry.hpp>
#include <subface/geometry/polyline_fillet.hpp>
namespace internal namespace internal
{ {
@ -16,9 +17,32 @@ void extrude_polyline_geometry_t::build()
const Eigen::Vector3d proj_y = axis_to_world.matrix().col(1).head<3>(); // Normal const Eigen::Vector3d proj_y = axis_to_world.matrix().col(1).head<3>(); // Normal
const Eigen::Vector3d origin = axis_to_world.matrix().col(3).head<3>(); const Eigen::Vector3d origin = axis_to_world.matrix().col(3).head<3>();
// 构建 profile 几何数据 // 对 profile 各衔接顶点插入微小圆角弧
// segment_ratio=0.05 意味着每段损失约 5%,棱角感仍然明显。
// 如需更锋利,将 segment_ratio 调至 0.01-0.02;
// 如需更平滑,调至 0.1-0.2。
std::vector<vector3d> filleted_pts;
std::vector<double> filleted_bgs;
const fillet_config_t fillet_cfg{/*segment_ratio=*/0.05,
/*min_turn_angle_deg=*/0.5};
insert_profile_corner_fillets(profile_desc, filleted_pts, filleted_bgs, fillet_cfg);
// 构建临时 descriptor 指向圆角后的数据(不修改原始 descriptor)
polyline_descriptor_t filleted_desc{};
filleted_desc.point_number = static_cast<uint32_t>(filleted_pts.size());
filleted_desc.points = filleted_pts.data();
filleted_desc.bulge_number = static_cast<uint32_t>(filleted_bgs.size());
filleted_desc.bulges = filleted_bgs.data();
filleted_desc.reference_normal = profile_desc.reference_normal;
filleted_desc.is_close = profile_desc.is_close;
std::cout << "[extrude_polyline_geometry_t::build] "
<< "original profile points: " << profile_desc.point_number << ", after fillet: " << filleted_desc.point_number
<< "\n";
// 构建 profile 几何数据(使用插入圆角后的 descriptor)
aabb_t_dim<2> profile_aabb; aabb_t_dim<2> profile_aabb;
profile_geom.build_as_profile(profile_desc, proj_x, proj_y, origin, profile_aabb); profile_geom.build_as_profile(filleted_desc, proj_x, proj_y, origin, profile_aabb);
// 将 profile 的最大范围扩展到 polyline_aabb // 将 profile 的最大范围扩展到 polyline_aabb
const auto profile_max_extent = profile_aabb.max().cwiseMax(profile_aabb.min().cwiseAbs()).maxCoeff(); const auto profile_max_extent = profile_aabb.max().cwiseMax(profile_aabb.min().cwiseAbs()).maxCoeff();

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

@ -0,0 +1,215 @@
#include <subface/geometry/polyline_fillet.hpp>
#include <math/math_defs.hpp>
//#include <algorithm>
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_profile_corner_fillets(const polyline_descriptor_t& profile,
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 = profile.point_number;
const bool closed = profile.is_close;
// ── 退化情况:点数过少,无内部衔接顶点可处理 ────────────────────────
// 闭合:至少需要 3 点才有顶点(含衔接)
// 不闭合:至少需要 3 点才有内部顶点(至少 1 个 interior vertex)
if (n < 3) {
for (uint32_t i = 0; i < n; ++i) {
out_points.push_back(profile.points[i]);
out_bulges.push_back(profile.bulges ? profile.bulges[i] : 0.0);
}
return;
}
std::vector<Eigen::Vector2d> pts(n);
std::vector<double> bulges(n);
for (uint32_t i = 0; i < n; ++i) {
pts[i] = {profile.points[i].x, profile.points[i].y};
bulges[i] = profile.bulges ? profile.bulges[i] : 0.0;
}
// ── 逐顶点计算圆角 ────────────────────────────────────────────────────
// 闭合:处理全部 n 个顶点(首尾环形相连)
// 不闭合:仅处理内部顶点 [1, n-2](首尾为端点,无入/出射配对)
std::vector<Corner> corners(n);
if (closed) {
for (uint32_t i = 0; i < n; ++i) {
const uint32_t prev = (i + n - 1) % n;
const uint32_t next = (i + 1) % n;
corners[i] = compute_corner(pts, bulges, i, prev, next, cfg);
}
} else {
// corners[0] 和 corners[n-1] 保持默认 inactive
for (uint32_t i = 1; i <= n - 2; ++i) { corners[i] = compute_corner(pts, bulges, i, i - 1, i + 1, cfg); }
}
out_points.reserve(n * 2);
out_bulges.reserve(n * 2);
if (closed) {
// 闭合:环形迭代,输出隐式首尾相连
//
// 对每段 i(pts[i]→pts[next],bulge=bulges[i]):
// 1.段起点:corners[i].active ? trim_out[i] : pts[i]
// 2.若 corners[next].active:插入 trim_in[next](附 fillet_bulge[next])
// 下一迭代自然从 trim_out[next] 开始
for (uint32_t i = 0; i < n; ++i) {
const uint32_t next = (i + 1) % n;
const double z_i = profile.points[i].z;
const double z_nxt = profile.points[next].z;
const Eigen::Vector2d& seg_start = corners[i].active ? corners[i].trim_out : pts[i];
push_point(out_points, out_bulges, seg_start, z_i, bulges[i]);
if (corners[next].active) {
push_point(out_points, out_bulges, corners[next].trim_in, z_nxt, corners[next].fillet_bulge);
}
}
} else {
// 不闭合:线性迭代,明确保留首尾端点
//
// 对每段 i(0..n-2),输出段起点;若 next 顶点有圆角(且非末点),
// 则额外插入 trim_in[next](附 fillet_bulge[next])作为本段实际终点,
// 下一迭代从 trim_out[next] 开始。
for (uint32_t i = 0; i < n - 1; ++i) {
const uint32_t next = i + 1;
const double z_i = profile.points[i].z;
const double z_nxt = profile.points[next].z;
// 段起点:
// i==0 → 始终为 pts[0](端点,没有圆角)
// corners[i].active → trim_out[i](由上一迭代的圆角偏移得到)
// 否则 → pts[i]
const Eigen::Vector2d& seg_start = (i == 0) ? pts[0] : corners[i].active ? corners[i].trim_out : pts[i];
// 若 next 有圆角(且 next 不是末点,末点无圆角):
// 本段实际终点为 trim_in[next],之后插入圆弧,
// 下一迭代从 trim_out[next] 开始
if (next < n - 1 && corners[next].active) {
push_point(out_points, out_bulges, seg_start, z_i, bulges[i]);
push_point(out_points, out_bulges, corners[next].trim_in, z_nxt, corners[next].fillet_bulge);
} else {
// next 无圆角(或 next 是末点):正常输出段起点
push_point(out_points, out_bulges, seg_start, z_i, bulges[i]);
}
}
// 末点:始终为 pts[n-1](端点,没有圆角)
// bulge 对最后一点无意义(无后续段),填 0.0
out_points.push_back(profile.points[n - 1]);
out_bulges.push_back(0.0);
}
assert(out_points.size() == out_bulges.size());
}
} // namespace internal
Loading…
Cancel
Save