|
|
|
@ -115,14 +115,8 @@ void polyline_geometry_data::build_internal(const polyline_descriptor_t& |
|
|
|
if (on_arc) { |
|
|
|
const Eigen::Vector2d ext_pt = c + r * Eigen::Vector2d(std::cos(ext_angle), std::sin(ext_angle)); |
|
|
|
aabb->extend(ext_pt); |
|
|
|
std::cout << " [ARC_AABB] adding extremum at " << (ext_angle * 180.0 / pi) << "° → (" << ext_pt[0] << "," |
|
|
|
<< ext_pt[1] << ")\n"; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 调试输出:AABB 当前状态
|
|
|
|
std::cout << " [ARC_AABB] after arc extrema: AABB min=(" << aabb->min().x() << "," << aabb->min().y() << ") max=(" |
|
|
|
<< aabb->max().x() << "," << aabb->max().y() << ")\n"; |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
@ -395,16 +389,13 @@ void polyline_geometry_data::build_as_axis(polyline_descriptor_t&& |
|
|
|
const auto local_y = base_vec2.dot(p_vec); |
|
|
|
double phi = std::atan2(local_y, local_x); |
|
|
|
if (theta > pi + 1e-9) { |
|
|
|
if (phi < -EPSILON) { phi += two_pi; } |
|
|
|
if (phi < 0.0) { phi += two_pi; } // 优弧:折叠到正值范围
|
|
|
|
} else { |
|
|
|
// 劣弧:phi 只应在 [0, theta],负值直接视为 gap
|
|
|
|
if (phi < -EPSILON) { |
|
|
|
phi = -1.0; // 强制触发端点回退
|
|
|
|
} |
|
|
|
if (phi < 0.0) { phi = -1.0; } // 劣弧:强制触发端点回退
|
|
|
|
} |
|
|
|
|
|
|
|
// 弧段越界时返回较近端点
|
|
|
|
if (phi < -1e-9 || phi > theta + 1e-9) { |
|
|
|
if (phi < 0.0 || phi > theta + 1e-9) { |
|
|
|
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(); |
|
|
|
@ -451,77 +442,61 @@ void polyline_geometry_data::build_as_axis(polyline_descriptor_t&& |
|
|
|
// Convention: return true = OUTSIDE (SDF > 0), false = INSIDE (SDF <= 0)
|
|
|
|
const Eigen::Vector2d vec = p - closest_param.point.topRows<2>(); |
|
|
|
|
|
|
|
if (closest_param.is_peak_value) { |
|
|
|
return vec.dot(this->calculate_normal(closest_param.t)) < 0; |
|
|
|
} |
|
|
|
if (closest_param.is_peak_value) { return vec.dot(this->calculate_normal(closest_param.t)) < 0; } |
|
|
|
|
|
|
|
constexpr double kChordEps = 1e-12; // 弦长退化阈值
|
|
|
|
constexpr double kCrossThresh = 1e-9; // 叉积共线阈值
|
|
|
|
constexpr double kCrossThresh = 1e-9; |
|
|
|
constexpr double kTangEps = 1e-7; |
|
|
|
const auto N = static_cast<uint32_t>(this->thetas.size()); |
|
|
|
auto n = static_cast<uint32_t>(std::round(closest_param.t)); |
|
|
|
const bool is_closed = (this->vertices.back() - this->vertices.front()).squaredNorm() < EPSILON; |
|
|
|
|
|
|
|
// 对闭合曲线,顶点索引 N 等同于顶点 0
|
|
|
|
if (is_closed && n >= N) n = 0u; |
|
|
|
|
|
|
|
// 顶点 i(0 ≤ i < N)的 2D 坐标存储在 vertices[start_indices[i]]。
|
|
|
|
// i == N 时(开放曲线末端),使用 vertices.back()。
|
|
|
|
auto get_vertex_2d = [&](uint32_t idx) -> Eigen::Vector2d { |
|
|
|
return (idx < N) ? this->vertices[this->start_indices[idx]] : this->vertices.back(); |
|
|
|
}; |
|
|
|
|
|
|
|
const Eigen::Vector2d v_curr = get_vertex_2d(n); |
|
|
|
|
|
|
|
Eigen::Vector2d v_next = v_curr; |
|
|
|
{ |
|
|
|
const uint32_t n1 = n + 1; |
|
|
|
if (n1 <= N) { |
|
|
|
v_next = get_vertex_2d(n1); |
|
|
|
} else if (is_closed) { |
|
|
|
v_next = get_vertex_2d(0); |
|
|
|
} |
|
|
|
// else: 开放曲线 n == N,无后继,v_next 保持 = v_curr
|
|
|
|
} |
|
|
|
|
|
|
|
Eigen::Vector2d v_prev = v_curr; |
|
|
|
if (n > 0u) { |
|
|
|
v_prev = get_vertex_2d(n - 1u); |
|
|
|
} else if (is_closed && N > 1u) { |
|
|
|
v_prev = get_vertex_2d(N - 1u); |
|
|
|
} |
|
|
|
// 入射切线:从段 n-1 的末端取(t = n - ε)
|
|
|
|
Eigen::Vector2d tang_in; |
|
|
|
if (n > 0u) |
|
|
|
tang_in = this->calculate_tangent(static_cast<double>(n) - kTangEps); |
|
|
|
else if (is_closed && N > 1u) |
|
|
|
tang_in = this->calculate_tangent(static_cast<double>(N) - kTangEps); |
|
|
|
else |
|
|
|
tang_in = this->calculate_tangent(kTangEps); // 开放曲线起点退化
|
|
|
|
|
|
|
|
// 出射切线:从段 n 的起始取(t = n + ε)
|
|
|
|
Eigen::Vector2d tang_out; |
|
|
|
if (n < N) |
|
|
|
tang_out = this->calculate_tangent(static_cast<double>(n) + kTangEps); |
|
|
|
else if (is_closed) |
|
|
|
tang_out = this->calculate_tangent(kTangEps); |
|
|
|
else |
|
|
|
tang_out = tang_in; // 开放曲线终点退化
|
|
|
|
|
|
|
|
const Eigen::Vector2d chord_out_raw = v_next - v_curr; |
|
|
|
const Eigen::Vector2d chord_in_raw = v_curr - v_prev; |
|
|
|
const double len_out = chord_out_raw.norm(); |
|
|
|
const double len_in = chord_in_raw.norm(); |
|
|
|
const double len_in = tang_in.norm(); |
|
|
|
const double len_out = tang_out.norm(); |
|
|
|
|
|
|
|
// 顶点重合,回退到弧/线法线
|
|
|
|
if (len_out < kChordEps && len_in < kChordEps) { |
|
|
|
const double t_fallback = std::min(static_cast<double>(n) + 1e-9, static_cast<double>(N) - 1e-9); |
|
|
|
return vec.dot(this->calculate_normal(t_fallback)) < 0; |
|
|
|
if (len_in < 1e-12 && len_out < 1e-12) { |
|
|
|
const double t_fb = std::min(static_cast<double>(n) + 1e-9, static_cast<double>(N) - 1e-9); |
|
|
|
return vec.dot(this->calculate_normal(t_fb)) < 0; |
|
|
|
} |
|
|
|
|
|
|
|
// 归一化(单边退化时用另一边代替)
|
|
|
|
const Eigen::Vector2d chord_out = (len_out >= kChordEps) ? (chord_out_raw / len_out) : (chord_in_raw / len_in); |
|
|
|
const Eigen::Vector2d chord_in = (len_in >= kChordEps) ? (chord_in_raw / len_in) : (chord_out_raw / len_out); |
|
|
|
const Eigen::Vector2d t_in = (len_in >= 1e-12) ? tang_in / len_in : tang_out / len_out; |
|
|
|
const Eigen::Vector2d t_out = (len_out >= 1e-12) ? tang_out / len_out : tang_in / len_in; |
|
|
|
|
|
|
|
const Eigen::Vector2d n_in_chord(-chord_in.y(), chord_in.x()); |
|
|
|
const Eigen::Vector2d n_out_chord(-chord_out.y(), chord_out.x()); |
|
|
|
// CCW 轮廓内向法线 = 切线的左法线
|
|
|
|
const Eigen::Vector2d n_in(-t_in.y(), t_in.x()); |
|
|
|
const Eigen::Vector2d n_out(-t_out.y(), t_out.x()); |
|
|
|
|
|
|
|
const double dot_in = vec.dot(n_in_chord); |
|
|
|
const double dot_out = vec.dot(n_out_chord); |
|
|
|
const double dot_in = vec.dot(n_in); |
|
|
|
const double dot_out = vec.dot(n_out); |
|
|
|
const double cross = t_in.x() * t_out.y() - t_in.y() * t_out.x(); |
|
|
|
|
|
|
|
const double cross = chord_in.x() * chord_out.y() - chord_in.y() * chord_out.x(); |
|
|
|
|
|
|
|
if (cross > kCrossThresh) { |
|
|
|
// 凸角
|
|
|
|
// 凸角:外部 = 两个外半平面的并集
|
|
|
|
return dot_in < 0.0 || dot_out < 0.0; |
|
|
|
} else if (cross < -kCrossThresh) { |
|
|
|
// 凹角
|
|
|
|
return dot_in < 0.0 && dot_out < 0.0; |
|
|
|
// 反凸/凹角:外部 = 两个外半平面的交集
|
|
|
|
return dot_in < 0.0 && dot_out < 0.0; |
|
|
|
} else { |
|
|
|
// 平角(切向共线,光滑连接)
|
|
|
|
// 两半平面方向近似相同,取双法线加权平均
|
|
|
|
// 近共线:平滑过渡
|
|
|
|
return (dot_in + dot_out) < 0.0; |
|
|
|
} |
|
|
|
} |
|
|
|
|