Compare commits

...

5 Commits

  1. 24
      primitive_process/interface/primitive_descriptor.h
  2. 25
      primitive_process/interface/subface/geometry/polyline_fillet.hpp
  3. 30
      primitive_process/src/subface/geometry/extrude_helixline_geometry.cpp
  4. 48
      primitive_process/src/subface/geometry/extrude_polyline_geometry.cpp
  5. 168
      primitive_process/src/subface/geometry/geometry_data.cpp
  6. 192
      primitive_process/src/subface/geometry/polyline_fillet.cpp
  7. 6
      primitive_process/src/subface/simple/extrude_helixline_side_face.cpp

24
primitive_process/interface/primitive_descriptor.h

@ -156,10 +156,10 @@ inline constexpr cylinder_descriptor_t unit_cylinder{
/// @brief 单位正方形 profile
inline const polyline_descriptor_t unit_square_profile = [] {
static const std::vector<vector3d> points = {
//{-0.5, -0.5, 0.0},
//{0.5, -0.5, 0.0},
//{0.5, 0.5, 0.0},
//{-0.5, 0.5, 0.0}
{-0.5, -0.5, 0.0},
{0.5, -0.5, 0.0},
{0.5, 0.5, 0.0},
{-0.5, 0.5, 0.0}
//{-1.5, -1.5, 0.0},
//{1.5, -1.5, 0.0},
@ -170,19 +170,19 @@ inline const polyline_descriptor_t unit_square_profile = [] {
//{2.0, -0.430871, 0.902414 }, // 局部坐标: (-1, -1)
//{2.0, 0.430871, -0.902414} // 局部坐标: (1, -1)
{0.0, 0.430871, -0.902414},
{0.0, -0.430871, 0.902414 },
{2.0, 0.1, -0.6},
{2.0, 0.430871, -0.902414}
//{0.0, 0.430871, -0.902414},
//{0.0, -0.430871, 0.902414 },
//{2.0, 0.1, -0.6},
//{2.0, 0.430871, -0.902414}
//{0.0, 0.430871, -0.902414}, // 局部坐标: (1, 1)
//{1.0, -0.430871, 0.902414 },
//{6.0, 0.430871, -0.902414} // 局部坐标: (1, -1)
};
// static const std::vector<double> bulges = {0.4, -0.8, 1.0, -0.5};
static const std::vector<double> bulges = {0.4, -0.8, 1.0, -0.5};
// static const std::vector<double> bulges = {-0.4, 0.8, 0.0, -0.5}; // polyline ERROR
// static const std::vector<double> bulges = {0.0, 0.0, -0.5, 0.0}; // helixline ERROR
static const std::vector<double> bulges = {0.0, 0.0, 1.0, 0.0}; // helixline ERROR
//static const std::vector<double> bulges = {0.0, 0.0, 1.0, 0.0}; // helixline ERROR
polyline_descriptor_t desc;
desc.point_number = 4;
desc.points = const_cast<vector3d*>(points.data());
@ -195,12 +195,12 @@ inline const polyline_descriptor_t unit_square_profile = [] {
inline const std::vector<vector3d> unit_axis_points = {
{0.0, 0.0, -0.5},
{0.0, 6.0, 6.0 }
{1.0, 0.0, 1.0 }
//{4.0, 0.0, 1.0}
//{4.0, 0.0, -1.0}
};
inline const std::vector<double> unit_axis_bulges = {0.0};
inline const std::vector<double> unit_axis_bulges = {0.0, 0.0};
inline const polyline_descriptor_t unit_polyline_axis = [] {
polyline_descriptor_t desc;

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

@ -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

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

@ -1,4 +1,5 @@
#include <subface/geometry/extrude_helixline_geometry.hpp>
#include <subface/geometry/polyline_fillet.hpp>
namespace internal
{
@ -11,21 +12,44 @@ void extrude_helixline_geometry_t::build()
// 构建 axis 几何数据
axis_geom.build_from_descriptor(axis_desc, axis_to_world, helixline_aabb);
// 对 profile 各衔接顶点插入微小圆角弧
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,
/*is_axis=*/true};
insert_polyline_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";
const auto& matrix_handle = this->axis_to_world.matrix();
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))
.normalized();
const Eigen::Vector3d B = N.cross(T);
// 构建 profile 几何数据(使用插入圆角后的 descriptor)
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()
<< ", max=" << profile_aabb.max().transpose() << std::endl;
const double profile_max_extent = std::max(profile_aabb.min().norm(), profile_aabb.max().norm());
helixline_aabb.min().array() -= profile_max_extent;
helixline_aabb.max().array() += profile_max_extent;
}
} // namespace internal

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

@ -1,24 +1,62 @@
#include <subface/geometry/extrude_polyline_geometry.hpp>
#include <subface/geometry/polyline_fillet.hpp>
#include <cmath>
namespace internal
{
void extrude_polyline_geometry_t::build()
{
const auto& profile_desc = this->descriptor.profiles[0];
const auto& axis_desc = this->descriptor.axis;
// 构建 axis 几何数据,同时得到 axis_to_world 坐标系
axis_geom.build_as_axis(axis_desc, profile_desc.reference_normal, axis_to_world, polyline_aabb);
std::vector<vector3d> filleted_axis_pts;
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);
polyline_descriptor_t filleted_axis_desc{};
filleted_axis_desc.point_number = static_cast<uint32_t>(filleted_axis_pts.size());
filleted_axis_desc.points = filleted_axis_pts.data();
filleted_axis_desc.bulge_number = static_cast<uint32_t>(filleted_axis_bgs.size());
filleted_axis_desc.bulges = filleted_axis_bgs.data();
filleted_axis_desc.reference_normal = axis_desc.reference_normal;
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);
std::vector<vector3d> filleted_profile_pts;
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);
polyline_descriptor_t filleted_profile_desc{};
filleted_profile_desc.point_number = static_cast<uint32_t>(filleted_profile_pts.size());
filleted_profile_desc.points = filleted_profile_pts.data();
filleted_profile_desc.bulge_number = static_cast<uint32_t>(filleted_profile_bgs.size());
filleted_profile_desc.bulges = filleted_profile_bgs.data();
filleted_profile_desc.reference_normal = profile_desc.reference_normal;
filleted_profile_desc.is_close = profile_desc.is_close;
std::cout << "[extrude_polyline_geometry_t::build] "
<< "profile: " << profile_desc.point_number << " pts → " << filleted_profile_desc.point_number
<< " pts (after fillet)\n";
// 从 axis_to_world 中提取投影方向
const Eigen::Vector3d proj_x = axis_to_world.matrix().col(0).head<3>(); // Binormal
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>();
// 构建 profile 几何数据
// 构建 profile 几何数据(使用插入圆角后的 descriptor)
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_profile_desc, proj_x, proj_y, origin, profile_aabb);
// 将 profile 的最大范围扩展到 polyline_aabb
const auto profile_max_extent = profile_aabb.max().cwiseMax(profile_aabb.min().cwiseAbs()).maxCoeff();

168
primitive_process/src/subface/geometry/geometry_data.cpp

@ -205,8 +205,10 @@ void polyline_geometry_data::build_as_axis(const polyline_descriptor_t&
profile_aabb);
const auto& profile_aabb_min = profile_aabb.min();
const auto& profile_aabb_max = profile_aabb.max();
aabb = aabb_t(Eigen::Vector3d(profile_aabb_min.y(), 0.0, profile_aabb_min.x()),
Eigen::Vector3d(profile_aabb_max.y(), 0.0, profile_aabb_max.x()));
aabb = aabb_t(
Eigen::Vector3d(profile_aabb_min.y(), 0.0, profile_aabb_min.x()),
Eigen::Vector3d(profile_aabb_max.y(), 0.0, profile_aabb_max.x())
);
}
void polyline_geometry_data::build_as_axis(polyline_descriptor_t&& desc,
@ -282,94 +284,26 @@ void polyline_geometry_data::build_as_axis(polyline_descriptor_t&&
frac = 1.0;
}
// 调试控制:每1000次调用打印一次调试信息
static int debug_counter = 0;
constexpr int DEBUG_INTERVAL = 10000;
bool should_debug = (++debug_counter % DEBUG_INTERVAL == 0);
if (should_debug) {
std::cout << "[DEBUG calculate_normal] 调用#" << debug_counter << " t=" << t << " n=" << n << " frac=" << frac
<< " 总段数=" << this->start_indices.size() << " 类型=" << (this->thetas[n] <= EPSILON ? "直线" : "圆弧")
<< std::endl;
}
const auto iter = this->vertices.begin() + this->start_indices[n];
if (this->thetas[n] <= EPSILON) {
// 直线段:计算切向量然后获取法向量
const auto temp = (*(iter + 1) - *iter).normalized();
if (should_debug) {
std::cout << "[DEBUG 直线段] 起点: (" << iter->x() << ", " << iter->y() << ")"
<< " 终点: (" << (iter + 1)->x() << ", " << (iter + 1)->y() << ")"
<< " 切向量: (" << temp.x() << ", " << temp.y() << ")"
<< " 切向量长度: " << temp.norm() << std::endl;
}
Eigen::Vector2d normal = {-temp.y(), temp.x()};
if (should_debug) {
std::cout << "[DEBUG 直线段] 法向量: (" << normal.x() << ", " << normal.y() << ")"
<< " 法向量长度: " << normal.norm() << std::endl;
}
return normal;
} else {
// 圆弧段:计算法向量方向
const Eigen::Vector2d vec_ca = *iter - *(iter + 1); // a - c
const Eigen::Vector2d vec_cd = *(iter + 2) - *(iter + 1); // d - c
if (should_debug) {
std::cout << "[DEBUG 圆弧段] 起点A: (" << iter->x() << ", " << iter->y() << ")"
<< " 圆心C: (" << (iter + 1)->x() << ", " << (iter + 1)->y() << ")"
<< " 中间点D: (" << (iter + 2)->x() << ", " << (iter + 2)->y() << ")"
<< " 终点B: (" << (iter + 3)->x() << ", " << (iter + 3)->y() << ")" << std::endl;
std::cout << "[DEBUG 圆弧段] vec_ca: (" << vec_ca.x() << ", " << vec_ca.y() << ") 长度: " << vec_ca.norm()
<< " vec_cd: (" << vec_cd.x() << ", " << vec_cd.y() << ") 长度: " << vec_cd.norm() << std::endl;
}
const double cross = vec_ca.x() * vec_cd.y() - vec_ca.y() * vec_cd.x();
const double bulge_sign = (cross > 1e-12) ? 1.0 : -1.0;
if (should_debug) {
std::cout << "[DEBUG 圆弧段] 叉积=" << cross << " 方向sign=" << bulge_sign << " theta=" << this->thetas[n] << "("
<< (this->thetas[n] * 180.0 / pi) << "°)"
<< " 半径=" << vec_ca.norm() << std::endl;
}
const auto alpha = std::cos(frac * this->thetas[n]);
const auto beta = std::sin(frac * this->thetas[n]);
if (should_debug) {
std::cout << "[DEBUG 圆弧段] 参数计算: frac*theta=" << (frac * this->thetas[n]) << "("
<< (frac * this->thetas[n] * 180.0 / pi) << "°)"
<< " alpha=cos=" << alpha << " beta=sin=" << beta << std::endl;
}
// 计算法向量
Eigen::Vector2d normal = bulge_sign * ((alpha + beta) * *(iter + 1) - alpha * *iter - beta * *(iter + 2));
if (should_debug) {
std::cout << "[DEBUG 圆弧段] 法向量: (" << normal.x() << ", " << normal.y() << ")"
<< " 长度: " << normal.norm() << std::endl;
// 检查法向量长度是否正常
double norm_length = normal.norm();
if (norm_length < 0.5 || norm_length > 2.0) {
std::cout << "[DEBUG 警告] 法向量长度异常: " << norm_length << std::endl;
// 输出详细分量信息用于调试
Eigen::Vector2d comp1 = (alpha + beta) * *(iter + 1);
Eigen::Vector2d comp2 = -alpha * *iter;
Eigen::Vector2d comp3 = -beta * *(iter + 2);
Eigen::Vector2d sum = comp1 + comp2 + comp3;
std::cout << "[DEBUG 分量详情] comp1=(" << comp1.x() << "," << comp1.y() << ")"
<< " comp2=(" << comp2.x() << "," << comp2.y() << ")"
<< " comp3=(" << comp3.x() << "," << comp3.y() << ")"
<< " 未缩放向量=(" << sum.x() << "," << sum.y() << ")" << std::endl;
}
}
return normal;
}
}
@ -389,11 +323,12 @@ void polyline_geometry_data::build_as_axis(polyline_descriptor_t&&
auto make_vertex_3d = [&](const Eigen::Vector2d& v) -> Eigen::Vector3d {
if (this->is_axis)
return {v.y(), 0., v.x()};
return {v.y(), 0., v.x()}; // Axis: (x,z) -> (z,0,x)
else
return {v.x(), v.y(), 0.};
return {v.x(), v.y(), 0.}; // Profile: (x,y) -> (x,y,0)
};
// 直线段最近点计算
auto line_cpm = [&](auto index) -> comparable_closest_param_t {
const auto iter = this->vertices.begin() + this->start_indices[index];
const auto line_vec = *(iter + 1) - *iter;
@ -412,48 +347,33 @@ void polyline_geometry_data::build_as_axis(polyline_descriptor_t&&
const auto raw_t = line_vec.dot(p_dir);
// 投影越界时直接返回较近端点
// raw_t ≤ 0 说明 P 在起点外侧,无需计算终点距离
if (raw_t <= 0) {
Eigen::Vector3d v = make_vertex_3d(*iter);
return {v, 0.0, (p - v).norm(), 1., false};
}
// raw_t ≥ len² 说明 P 在终点外侧,无需计算起点距离
if (raw_t >= line_vec_length_2) {
Eigen::Vector3d v = make_vertex_3d(*(iter + 1));
return {v, 1.0, (p - v).norm(), 1., false};
}
const auto p_dir_length_2 = p_dir.squaredNorm();
const auto t = raw_t / line_vec_length_2;
Eigen::Vector3d closest_point_3d;
double dist;
// 计算最近点
const Eigen::Vector2d closest_2d = *iter + t * line_vec;
Eigen::Vector3d closest_point_3d = make_vertex_3d(closest_2d);
if (this->is_axis) {
// Axis
closest_point_3d.x() = (1 - t) * iter->y() + t * (iter + 1)->y();
closest_point_3d.y() = 0;
closest_point_3d.z() = (1 - t) * iter->x() + t * (iter + 1)->x();
dist = (p - closest_point_3d).norm();
} else {
// Profile
closest_point_3d.x() = (1 - t) * iter->x() + t * (iter + 1)->x();
closest_point_3d.y() = (1 - t) * iter->y() + t * (iter + 1)->y();
closest_point_3d.z() = 0;
double dist = (p - closest_point_3d).norm();
dist = std::sqrt(p_dir_length_2 - raw_t * raw_t / line_vec_length_2 + p[2] * p[2]);
}
return {closest_point_3d, t, dist, 1., true};
};
// 圆弧段最近点计算
auto circle_cpm = [&](auto index) -> comparable_closest_param_t {
const auto iter = this->vertices.begin() + this->start_indices[index];
const auto theta = this->thetas[index];
const auto base_vec1 = *iter - *(iter + 1); // 起点 - 圆心
const auto base_vec2 = *(iter + 2) - *(iter + 1); // 切线辅助点 d - 圆心 c(非弧终点 b
const auto base_vec2 = *(iter + 2) - *(iter + 1); // 切线辅助点 d - 圆心 c
Eigen::Vector2d p_vec;
double cross_bv = base_vec1.x() * base_vec2.y() - base_vec1.y() * base_vec2.x();
bool is_cw = (cross_bv < 0);
if (this->is_axis) {
p_vec = Eigen::Vector2d(p[2], p[0]) - *(iter + 1);
} else {
@ -462,33 +382,18 @@ void polyline_geometry_data::build_as_axis(polyline_descriptor_t&&
const auto p_vec_norm = p_vec.norm(); // 查询点到圆心的距离
const auto r = base_vec1.norm(); // 圆弧半径
const double off_plane = this->is_axis ? p[1] : p[2];
if (p_vec_norm < EPSILON) {
// 查询点恰好在圆心,选弧段起点
const auto closest_point = *iter;
const Eigen::Vector3d closest_point = make_vertex_3d(*iter);
const auto dis_on_plane = -r;
return {
{closest_point.x(), closest_point.y(), 0},
0.0,
std::sqrt(p[2] * p[2] + dis_on_plane * dis_on_plane),
1.,
false
};
return {closest_point, 0.0, std::sqrt(off_plane * off_plane + dis_on_plane * dis_on_plane), 1., false};
}
const auto local_x = base_vec1.dot(p_vec);
const auto local_y = base_vec2.dot(p_vec);
double phi = std::atan2(local_y, local_x);
// [DEBUG_CIRCLE_CPM] 对凹弧(CW)打印 phi 分布,验证是否全为负
{
static std::atomic<int> cw_cnt{0};
if (is_cw && ++cw_cnt <= 100) {
std::cout << "[DEBUG_CIRCLE_CPM] CW弧 #" << cw_cnt << " seg=" << index << " phi=" << phi << " theta=" << theta
<< " phi_in_range=[" << (-theta) << "," << 0 << "]"
<< " cross_bv=" << cross_bv << " p_vec=(" << p_vec.x() << "," << p_vec.y() << ")"
<< " base_vec1=(" << base_vec1.x() << "," << base_vec1.y() << ")"
<< " base_vec2=(" << base_vec2.x() << "," << base_vec2.y() << ")"
<< " -> fallback_to_endpoint=" << (phi < -EPSILON || phi > theta + 1e-9 ? "YES" : "NO") << "\n";
}
}
if (theta > pi + 1e-9) {
if (phi < -EPSILON) { phi += two_pi; }
} else {
@ -500,8 +405,8 @@ void polyline_geometry_data::build_as_axis(polyline_descriptor_t&&
// 弧段越界时返回较近端点
if (phi < -1e-9 || phi > theta + 1e-9) {
const Eigen::Vector3d start_3d = {iter->x(), iter->y(), 0.};
const Eigen::Vector3d end_3d = {(iter + 3)->x(), (iter + 3)->y(), 0.};
const Eigen::Vector3d start_3d = make_vertex_3d(*iter);
const Eigen::Vector3d end_3d = make_vertex_3d(*(iter + 3));
const double dist_start = (p - start_3d).norm();
const double dist_end = (p - end_3d).norm();
if (dist_start <= dist_end)
@ -511,18 +416,19 @@ void polyline_geometry_data::build_as_axis(polyline_descriptor_t&&
}
const auto dis_on_plane = p_vec_norm - r;
const auto closest_point = p_vec / p_vec_norm * r + *(iter + 1);
const Eigen::Vector2d closest_2d = p_vec / p_vec_norm * r + *(iter + 1);
Eigen::Vector3d closest_3d = make_vertex_3d(closest_2d);
return {
{closest_point.x(), closest_point.y(), 0},
closest_3d,
phi / theta,
std::sqrt(p[2] * p[2] + dis_on_plane * dis_on_plane),
std::sqrt(off_plane * off_plane + dis_on_plane * dis_on_plane),
1.,
true
};
};
// 单次遍历
// 计算每个线段的最近点
std::vector<comparable_closest_param_t> closest_params(this->thetas.size());
std::vector<size_t> indices(this->thetas.size());
std::iota(indices.begin(), indices.end(), 0);
@ -784,30 +690,6 @@ Eigen::Vector3d helixline_geometry_data::calculate_normal(double t) const
// return {f0 / (f0 - f1), true, true};
return {t0 + (t1 - t0) * f0 / (f0 - f1), true, true};
});
// [JUMP_DEBUG] 检测是否存在近似等距的第二候选点
const double best_sq = sq_dist_at(peak_value_param.t);
double second_sq = std::numeric_limits<double>::max();
double second_t = -1.0;
for (int64_t k_ = static_cast<int64_t>(rounded_times_start); k_ <= static_cast<int64_t>(rounded_times_end); ++k_) {
const double t_cand = (theta_p + pi / 2.0 + k_ * pi) * inv_line_theta;
if (t_cand < 0.0 || t_cand > 1.0) continue;
const double sq = sq_dist_at(t_cand);
if (sq > best_sq + 1e-14 && sq < second_sq) {
second_sq = sq;
second_t = t_cand;
}
}
const double gap = std::sqrt(second_sq) - std::sqrt(best_sq);
if (gap < 0.05 && std::sqrt(best_sq) < this->radius * 2.0) { // 仅关注近表面
static std::atomic<int> cnt{0};
if (++cnt <= 100) {
std::cout << "[JUMP_WARN] #" << cnt << " best_t=" << peak_value_param.t << " dist=" << std::sqrt(best_sq)
<< " | second_t=" << second_t << " dist=" << std::sqrt(second_sq) << " gap=" << gap << "\n";
}
}
}
// if we need to refine the guess, just do it!!!

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

@ -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

6
primitive_process/src/subface/simple/extrude_helixline_side_face.cpp

@ -210,12 +210,6 @@ std::tuple<Eigen::Vector4d, double, double> extrude_helixline_side_function_impl
Eigen::Vector3d axis_normal = face->get_axis_geom().calculate_normal(t_axis);
Eigen::Vector3d B = axis_normal.cross(axis_tangent).normalized();
//Eigen::Transform<double, 3, Eigen::Isometry> TBN;
//TBN.matrix().col(0).head<3>() = B;
//TBN.matrix().col(1).head<3>() = axis_normal;
//TBN.matrix().col(2).head<3>() = axis_tangent;
//TBN.matrix().col(3).head<3>() = axis_point;
//TBN.matrix()(3, 3) = 1.0;
auto TBN = get_local_TBN_helixline(face->get_axis_geom(), axis_point, t_axis);
// ==================== 处理 Profile 参数 ====================

Loading…
Cancel
Save