Browse Source

feat: simple arc self-intersection detection

main
linchforever 2 days ago
parent
commit
376d67207b
  1. 10
      primitive_process/interface/subface/geometry/polyline_fillet.hpp
  2. 126
      primitive_process/src/subface/geometry/polyline_fillet.cpp

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

@ -19,4 +19,14 @@ void insert_polyline_corner_fillets(const polyline_descriptor_t& profile,
std::vector<double>& out_bulges,
const fillet_config_t& cfg = {});
/**
* @brief polyline bulge
* bulge 0退线 PMC
*/
void sanitize_negative_bulge_self_intersections(const std::vector<vector3d>& pts_3d,
std::vector<double>& bulges,
bool is_axis,
bool closed);
} // namespace internal

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

@ -39,7 +39,6 @@ Eigen::Vector2d arc_start_tangent(Eigen::Vector2d A, Eigen::Vector2d B, double b
return rotate2d(d, -sign * theta / 2.0);
}
struct Corner {
bool active = false;
Eigen::Vector2d trim_in; // 圆弧起点
@ -97,7 +96,6 @@ Corner compute_corner(const std::vector<Eigen::Vector2d>& pts,
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;
@ -153,11 +151,19 @@ static double recompute_subarc_bulge(const Eigen::Vector2d& A,
// 规范化:子弧行进方向与原弧一致(CCW 为正,CW 为负)
if (sign_b > 0.0) {
if (delta < 0.0) delta += 2.0 * pi;
if (delta > 2.0 * pi) delta -= 2.0 * pi;
// 强制 delta 落入 (0, 2π]
delta = std::fmod(delta, 2.0 * pi);
if (delta <= 0.0) delta += 2.0 * pi;
} else {
if (delta > 0.0) delta -= 2.0 * pi;
if (delta < -2.0 * pi) delta += 2.0 * pi;
// 强制 delta 落入 [-2π, 0)
delta = std::fmod(delta, 2.0 * pi);
if (delta >= 0.0) delta -= 2.0 * pi;
}
double result = std::tan(delta / 4.0);
if ((b > 0) != (result > 0) && std::abs(result) > 1e-9) {
std::cerr << "[BULGE_SIGN_FLIP] original_b=" << b << " recomputed_bulge=" << result << " delta=" << delta
<< " ang_A=" << ang_A << " ang_B=" << ang_B << "\n";
}
return std::tan(delta / 4.0);
@ -173,8 +179,114 @@ inline void push_point(std::vector<vector3d>& out_points,
out_bulges.push_back(bulge);
}
/**
* @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)
{
const Eigen::Vector2d ab = b - a;
const double len2 = ab.squaredNorm();
if (len2 < 1e-24) return (p - a).norm();
const double t = std::clamp((p - a).dot(ab) / len2, 0.0, 1.0);
return (p - (a + t * ab)).norm();
}
} // anonymous namespace
/**
* @brief polyline "负 bulge 圆弧与相邻边发生自相交"
*
* @details
* bulgeCW CCW profile 线
* < RPMC
* SDF
*
* 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 bulges bulge 0
* @param is_axis true = XZ Axisfalse = XY Profile
* @param closed 线
*/
void sanitize_negative_bulge_self_intersections(const std::vector<vector3d>& pts_3d,
std::vector<double>& bulges,
bool is_axis,
bool closed)
{
const auto n = static_cast<uint32_t>(pts_3d.size());
if (n < 2) return;
// 将顶点投影到对应的 2D 工作平面
std::vector<Eigen::Vector2d> pts(n);
for (uint32_t i = 0; i < n; ++i) {
if (is_axis)
pts[i] = {pts_3d[i].z, pts_3d[i].x}; // Axis: XZ 平面 → (Z, X)
else
pts[i] = {pts_3d[i].x, pts_3d[i].y}; // Profile: XY 平面 → (X, Y)
}
const uint32_t seg_count = closed ? n : (n - 1u);
for (uint32_t i = 0; i < seg_count; ++i) {
if (bulges[i] >= 0.0) continue; // 仅检查负 bulge 的内凹弧
const uint32_t i1 = (i + 1u) % n;
const Eigen::Vector2d& A = pts[i];
const Eigen::Vector2d& B = pts[i1];
const double b = bulges[i];
const double chord_len = (B - A).norm();
if (chord_len < 1e-12) continue;
// 计算 CW 弧的圆心与半径
// half_theta = theta/2,其中 theta = 4*atan(|b|) 为弧所对圆心角
const double abs_b = std::abs(b);
const double half_theta = 2.0 * std::atan(abs_b);
const double R = chord_len / (2.0 * std::sin(half_theta));
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 double d_center = R * std::cos(half_theta); // 弦中点到圆心的距离
const Eigen::Vector2d center = 0.5 * (A + B) + d_center * perp_right;
bool self_intersects = false;
// ---- 检测与前一段 [pts[i-1], A] 的自相交 ----
if (closed || i > 0u) {
const uint32_t i0 = (i + n - 1u) % n;
const double dist = point_to_segment_dist_2d(center, pts[i0], A);
if (dist < R * (1.0 - 1e-6)) {
self_intersects = true;
std::cout << "[SANITIZE_ARC] 段[" << i << "] 负bulge圆弧"
<< "(R=" << R << ", bulge=" << b << ""
<< "圆心到前段距离=" << dist << " < R,"
<< "检测到与前段自相交,bulge → 0(退化直线)\n";
}
}
// ---- 检测与后一段 [B, pts[i+2]] 的自相交 ----
if (!self_intersects && (closed || i + 2u < n)) {
const uint32_t i2 = (i + 2u) % n;
const double dist = point_to_segment_dist_2d(center, B, pts[i2]);
if (dist < R * (1.0 - 1e-6)) {
self_intersects = true;
std::cout << "[SANITIZE_ARC] 段[" << i << "] 负bulge圆弧"
<< "(R=" << R << ", bulge=" << b << ""
<< "圆心到后段距离=" << dist << " < R,"
<< "检测到与后段自相交,bulge → 0(退化直线)\n";
}
}
if (self_intersects) { bulges[i] = 0.0; }
}
}
void insert_polyline_corner_fillets(const polyline_descriptor_t& desc,
std::vector<vector3d>& out_points,
@ -195,7 +307,7 @@ void insert_polyline_corner_fillets(const polyline_descriptor_t& desc,
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)
}; // 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}; };

Loading…
Cancel
Save