You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
350 lines
13 KiB
350 lines
13 KiB
#pragma once
|
|
#include <data/data_center.hpp>
|
|
#include <base/primitive.hpp>
|
|
#include "subface/simple/extrude_polyline_side_face.hpp"
|
|
#include "subface/simple/extrude_helixline_side_face.hpp"
|
|
#include <subface/simple/plane.hpp>
|
|
|
|
namespace internal
|
|
{
|
|
inline void expand_aabb_for_bulge_segment(const vector2d& A,
|
|
const vector2d& B,
|
|
double bulge,
|
|
vector2d& aabb_min,
|
|
vector2d& aabb_max)
|
|
{
|
|
// 端点始终纳入(无论是直线还是圆弧,端点必在 AABB 内)
|
|
aabb_min.x = std::min({aabb_min.x, A.x, B.x});
|
|
aabb_min.y = std::min({aabb_min.y, A.y, B.y});
|
|
aabb_max.x = std::max({aabb_max.x, A.x, B.x});
|
|
aabb_max.y = std::max({aabb_max.y, A.y, B.y});
|
|
|
|
if (std::abs(bulge) < 1e-12) return; // 直线段,端点已处理完毕
|
|
|
|
// 计算圆心与半径
|
|
Eigen::Vector2d Av(A.x, A.y), Bv(B.x, B.y);
|
|
Eigen::Vector2d chord = Bv - Av;
|
|
double chord_len = chord.norm();
|
|
if (chord_len < 1e-12) return;
|
|
|
|
Eigen::Vector2d chord_dir = chord / chord_len;
|
|
Eigen::Vector2d chord_perp(-chord_dir.y(), chord_dir.x()); // 左法向
|
|
|
|
// d = chord/2 * (1/bulge - bulge) / 2 = chord*(1-b²)/(4b)
|
|
double d = 0.5 * chord_len * (1.0 / bulge - bulge) * 0.5;
|
|
Eigen::Vector2d M = 0.5 * (Av + Bv);
|
|
Eigen::Vector2d center = M + chord_perp * d;
|
|
double radius = std::sqrt(d * d + 0.25 * chord_len * chord_len);
|
|
|
|
// 计算端点对应的极角
|
|
double angle_A = std::atan2(A.y - center.y(), A.x - center.x());
|
|
double angle_B = std::atan2(B.y - center.y(), B.x - center.x());
|
|
|
|
// 判断某极角是否在弧段内
|
|
// bulge > 0 → CCW(从 angle_A 逆时针到 angle_B)
|
|
// bulge < 0 → CW (从 angle_A 顺时针到 angle_B)
|
|
auto angle_in_arc = [&](double theta) -> bool {
|
|
// 将所有角度归一化到 [0, 2π)
|
|
auto norm2pi = [](double a) -> double {
|
|
a = std::fmod(a, 2.0 * pi);
|
|
return a < 0.0 ? a + 2.0 * pi : a;
|
|
};
|
|
double sa = norm2pi(angle_A);
|
|
double ea = norm2pi(angle_B);
|
|
double ta = norm2pi(theta);
|
|
|
|
if (bulge > 0.0) { // CCW: sa → ea,增角方向
|
|
if (sa <= ea)
|
|
return ta >= sa && ta <= ea;
|
|
else
|
|
return ta >= sa || ta <= ea; // 跨过 0°/360°
|
|
} else { // CW: sa → ea,减角方向
|
|
if (sa >= ea)
|
|
return ta <= sa && ta >= ea;
|
|
else
|
|
return ta <= sa || ta >= ea; // 跨过 0°/360°
|
|
}
|
|
};
|
|
|
|
// 检查四个坐标轴极值方向(0°, 90°, 180°, 270°)
|
|
struct {
|
|
double angle;
|
|
double dx;
|
|
double dy;
|
|
} extrema[] = {
|
|
{0.0, radius, 0.0 },
|
|
{pi / 2.0, 0.0, radius },
|
|
{pi, -radius, 0.0 },
|
|
{3.0 * pi / 2.0, 0.0, -radius},
|
|
};
|
|
|
|
for (const auto& e : extrema) {
|
|
if (angle_in_arc(e.angle)) {
|
|
aabb_min.x = std::min(aabb_min.x, center.x() + e.dx);
|
|
aabb_min.y = std::min(aabb_min.y, center.y() + e.dy);
|
|
aabb_max.x = std::max(aabb_max.x, center.x() + e.dx);
|
|
aabb_max.y = std::max(aabb_max.y, center.y() + e.dy);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 计算单段 bulge 圆弧的 AABB 扩展
|
|
// bulge = tan(θ/4), θ = 圆弧包角
|
|
// 圆心: c = M + perp * d, 其中 perp 为弦的单位法向, d = (chord/2)(1/bulge - bulge)/2
|
|
inline vector2d compute_profile_aabb_min(const polyline_descriptor_t* profile)
|
|
{
|
|
if (!profile || profile->point_number == 0) return {0.0, 0.0};
|
|
|
|
vector2d min_val{std::numeric_limits<double>::max(), std::numeric_limits<double>::max()};
|
|
vector2d max_val{std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest()};
|
|
|
|
for (uint32_t i = 0; i < profile->point_number; ++i) {
|
|
min_val.x = std::min(min_val.x, profile->points[i].x);
|
|
min_val.y = std::min(min_val.y, profile->points[i].y);
|
|
max_val.x = std::max(max_val.x, profile->points[i].x);
|
|
max_val.y = std::max(max_val.y, profile->points[i].y);
|
|
}
|
|
|
|
if (profile->bulges) {
|
|
uint32_t n = profile->point_number;
|
|
for (uint32_t i = 0; i < n; ++i) {
|
|
double bulge = profile->bulges[i];
|
|
if (std::abs(bulge) < 1e-12) continue;
|
|
uint32_t j = (i + 1) % n;
|
|
|
|
vector2d A_2d = {profile->points[i].x, profile->points[i].y};
|
|
vector2d B_2d = {profile->points[j].x, profile->points[j].y};
|
|
expand_aabb_for_bulge_segment(A_2d, B_2d, bulge, min_val, max_val);
|
|
}
|
|
}
|
|
|
|
constexpr double margin = 1e-6;
|
|
min_val.x -= margin;
|
|
min_val.y -= margin;
|
|
return min_val;
|
|
}
|
|
|
|
inline vector2d compute_profile_aabb_max(const polyline_descriptor_t* profile)
|
|
{
|
|
if (!profile || profile->point_number == 0) return {0.0, 0.0};
|
|
|
|
vector2d min_val{std::numeric_limits<double>::max(), std::numeric_limits<double>::max()};
|
|
vector2d max_val{std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest()};
|
|
|
|
for (uint32_t i = 0; i < profile->point_number; ++i) {
|
|
min_val.x = std::min(min_val.x, profile->points[i].x);
|
|
min_val.y = std::min(min_val.y, profile->points[i].y);
|
|
max_val.x = std::max(max_val.x, profile->points[i].x);
|
|
max_val.y = std::max(max_val.y, profile->points[i].y);
|
|
}
|
|
|
|
if (profile->bulges) {
|
|
uint32_t n = profile->point_number;
|
|
for (uint32_t i = 0; i < n; ++i) {
|
|
double bulge = profile->bulges[i];
|
|
if (std::abs(bulge) < 1e-12) continue;
|
|
uint32_t j = (i + 1) % n;
|
|
|
|
// 修改处:从vector3d转换为vector2d
|
|
vector2d A_2d = {profile->points[i].x, profile->points[i].y};
|
|
vector2d B_2d = {profile->points[j].x, profile->points[j].y};
|
|
expand_aabb_for_bulge_segment(A_2d, B_2d, bulge, min_val, max_val);
|
|
}
|
|
}
|
|
|
|
constexpr double margin = 1e-6;
|
|
max_val.x += margin;
|
|
max_val.y += margin;
|
|
return max_val;
|
|
}
|
|
|
|
/**
|
|
* @brief 从 polyline descriptor 计算底盖 matrix
|
|
* @param desc polyline descriptor
|
|
* @return 底盖的 paired_model_matrix
|
|
*/
|
|
inline paired_model_matrix compute_polyline_bottom_cap_matrix(const polyline_descriptor_t& desc)
|
|
{
|
|
Eigen::Vector3d origin = Eigen::Map<const Eigen::Vector3d>(&desc.points[0].x);
|
|
Eigen::Vector3d p1 = Eigen::Map<const Eigen::Vector3d>(&desc.points[1].x);
|
|
Eigen::Vector3d tangent = (p1 - origin).normalized();
|
|
Eigen::Vector3d axis_normal = Eigen::Map<const Eigen::Vector3d>(&desc.reference_normal.x);
|
|
axis_normal.normalize();
|
|
|
|
return build_polyline_cap_matrix(origin, tangent, axis_normal, false);
|
|
}
|
|
|
|
/**
|
|
* @brief 从 polyline descriptor 计算顶盖 matrix
|
|
* @param desc polyline descriptor
|
|
* @return 顶盖的 paired_model_matrix
|
|
*/
|
|
inline paired_model_matrix compute_polyline_top_cap_matrix(const polyline_descriptor_t& desc)
|
|
{
|
|
uint32_t last_idx = desc.point_number - 1;
|
|
Eigen::Vector3d origin = Eigen::Map<const Eigen::Vector3d>(&desc.points[last_idx].x);
|
|
Eigen::Vector3d p_prev = Eigen::Map<const Eigen::Vector3d>(&desc.points[last_idx - 1].x);
|
|
Eigen::Vector3d tangent = (origin - p_prev).normalized();
|
|
Eigen::Vector3d axis_normal = Eigen::Map<const Eigen::Vector3d>(&desc.reference_normal.x);
|
|
axis_normal.normalize();
|
|
|
|
return build_polyline_cap_matrix(origin, tangent, axis_normal, true);
|
|
}
|
|
|
|
/**
|
|
* @brief 从螺旋线几何数据计算端盖变换矩阵
|
|
* @param ag 螺旋线几何数据
|
|
* @param t 参数化位置 (0.0 表示起点, 1.0 表示终点)
|
|
* @return 端盖的 paired_model_matrix
|
|
*
|
|
* 这个函数计算螺旋线在参数 t 处的 Frenet 标架(切线、法线、副法线),
|
|
* 并构建从局部坐标系到世界坐标系的变换矩阵。
|
|
* 切线方向作为平面的法向,指向几何体内部。
|
|
*/
|
|
inline paired_model_matrix make_helixline_cap_matrix(const helixline_geometry_data& ag, double t)
|
|
{
|
|
// 计算曲线端点处的切线 / 法线 / 副法线
|
|
const Eigen::Vector3d T = ag.calculate_tangent(t);
|
|
const Eigen::Vector3d N = ag.calculate_normal(t);
|
|
const Eigen::Vector3d B = N.cross(T);
|
|
|
|
// 计算曲线端点的世界坐标
|
|
const double angle = t * ag.total_theta;
|
|
const Eigen::Vector3d P(ag.radius * std::cos(angle), ag.radius * std::sin(angle), t * ag.height);
|
|
|
|
paired_model_matrix m;
|
|
auto& mat = m.local_to_world.matrix();
|
|
mat.setIdentity();
|
|
mat.col(0).head<3>() = T; // 平面法向 = 切线方向(指向几何体内部)
|
|
mat.col(1).head<3>() = N; // 平面内轴
|
|
mat.col(2).head<3>() = B; // 平面内轴
|
|
mat.col(3).head<3>() = P;
|
|
m.world_to_local = m.local_to_world.inverse();
|
|
return m;
|
|
}
|
|
|
|
|
|
inline paired_model_matrix compute_helixline_bottom_cap_matrix(const helixline_geometry_data& ag)
|
|
{
|
|
return make_helixline_cap_matrix(ag, 0.0);
|
|
}
|
|
|
|
/**
|
|
* @brief 从 helixline descriptor 计算顶盖 matrix
|
|
* @param ag 螺旋线几何数据
|
|
* @return 顶盖的 paired_model_matrix
|
|
*/
|
|
inline paired_model_matrix compute_helixline_top_cap_matrix(const helixline_geometry_data& ag)
|
|
{
|
|
return make_helixline_cap_matrix(ag, 1.0);
|
|
}
|
|
|
|
// ==================== Polyline 拉伸体 ====================
|
|
|
|
/**
|
|
* @brief Polyline 路径的拉伸体
|
|
*/
|
|
struct extrude_polyline_t final : primitive {
|
|
extrude_polyline_t(primitive_data_center_t* data_center_ptr) : primitive(data_center_ptr)
|
|
{
|
|
// 使用默认 descriptor 初始化
|
|
extrude_polyline_descriptor_t full_desc{};
|
|
full_desc.profile_number = 1;
|
|
full_desc.profiles = const_cast<polyline_descriptor_t*>(&descriptor_defaults::unit_square_profile);
|
|
full_desc.axis = descriptor_defaults::unit_polyline_axis;
|
|
|
|
initialize_from_descriptor(data_center_ptr, full_desc);
|
|
}
|
|
|
|
primitive_type get_type() const override { return PRIMITIVE_TYPE_EXTRUDE_POLYLINE; }
|
|
|
|
stl_vector_mp<const void*> get_subface_geometries() const override;
|
|
|
|
span<marked_subface_ptr_t<subface>> get_subfaces() const override
|
|
{
|
|
return {const_cast<marked_subface_ptr_t<subface>*>(subfaces.data()), subfaces.size()};
|
|
}
|
|
|
|
stl_vector_mp<surface_type> get_subface_types() const override
|
|
{
|
|
return {
|
|
surface_type::extrude_polyline_side,
|
|
surface_type::plane, // 底盖
|
|
surface_type::plane // 顶盖
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @brief 从 descriptor 初始化(内部方法,类似 cylinder 的 initialize)
|
|
* @details 先准备好所有参数,然后一次性调用基类的 initialize
|
|
*/
|
|
PE_API void initialize_from_descriptor(primitive_data_center_t* dc, const extrude_polyline_descriptor_t& desc);
|
|
|
|
/**
|
|
* @brief 使用 Profile 和 Axis 初始化拉伸体
|
|
*/
|
|
PE_API void initialize_with_components(primitive_data_center_t* dc, const extrude_polyline_descriptor_t& desc)
|
|
{
|
|
initialize_from_descriptor(dc, desc);
|
|
}
|
|
|
|
/**
|
|
* @brief 替换 Profile
|
|
* @details 保持 Axis 不变,仅更新 Profile
|
|
*/
|
|
PE_API void replace_profile(const profile_descriptor_t& new_profile);
|
|
|
|
/**
|
|
* @brief 替换 Axis
|
|
* @details 保持 Profile 不变,仅更新 Axis
|
|
*/
|
|
PE_API void replace_axis(const axis_descriptor_t& new_axis);
|
|
|
|
// 3个子面:侧面 + 底盖 + 顶盖
|
|
std::array<marked_subface_ptr_t<subface>, 3> subfaces{};
|
|
};
|
|
|
|
// ==================== Helixline 拉伸体 ====================
|
|
|
|
/**
|
|
* @brief Helixline 路径的拉伸体
|
|
*/
|
|
struct extrude_helixline_t final : primitive {
|
|
extrude_helixline_t(primitive_data_center_t* data_center_ptr) : primitive(data_center_ptr)
|
|
{
|
|
// 使用默认 descriptor 初始化
|
|
extrude_helixline_descriptor_t full_desc{};
|
|
full_desc.profile_number = 1;
|
|
full_desc.profiles = const_cast<polyline_descriptor_t*>(&descriptor_defaults::unit_square_profile);
|
|
full_desc.axis = descriptor_defaults::unit_helix;
|
|
initialize_from_descriptor(data_center_ptr, full_desc);
|
|
}
|
|
|
|
primitive_type get_type() const override { return PRIMITIVE_TYPE_EXTRUDE_HELIXLINE; }
|
|
|
|
stl_vector_mp<const void*> get_subface_geometries() const override;
|
|
|
|
span<marked_subface_ptr_t<subface>> get_subfaces() const override
|
|
{
|
|
return {const_cast<marked_subface_ptr_t<subface>*>(subfaces.data()), 3};
|
|
}
|
|
|
|
stl_vector_mp<surface_type> get_subface_types() const override
|
|
{
|
|
return {surface_type::extrude_helixline_side, surface_type::plane, surface_type::plane};
|
|
}
|
|
|
|
PE_API void initialize_from_descriptor(primitive_data_center_t* dc, const extrude_helixline_descriptor_t& desc);
|
|
|
|
PE_API void initialize_with_components(primitive_data_center_t* dc, const extrude_helixline_descriptor_t& desc)
|
|
{
|
|
initialize_from_descriptor(dc, desc);
|
|
}
|
|
|
|
PE_API void replace_profile(const profile_descriptor_t& new_profile);
|
|
PE_API void replace_axis(const axis_descriptor_t& new_axis);
|
|
|
|
std::array<marked_subface_ptr_t<subface>, 3> subfaces{};
|
|
};
|
|
|
|
} // namespace internal
|