extract explicit mesh with topology information from implicit surfaces with boolean operations, and do surface/volume integrating on them.
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.
 
 
 
 
 
 

184 lines
7.9 KiB

#include <primitive/pattern/polyline_pattern.hpp>
namespace internal
{
double polyline_pattern::eval_sdf(Eigen::Vector2d p) const
{
auto line_cpm = [&](auto index) {
const auto a = this->vertices[index];
const auto b = this->vertices[index + 1];
const Eigen::Vector2d line_vec = b - a;
const Eigen::Vector2d ap = p - a;
const Eigen::Vector2d bp = p - b;
// 计算投影参数
const auto t = std::clamp(line_vec.dot(ap) / line_vec.squaredNorm(), .0, 1.);
const Eigen::Vector2d proj = a + t * line_vec;
const auto dist = (p - proj).norm();
// 计算GWN
const auto dot = ap.dot(bp);
const auto cross = ap.x() * bp.y() - ap.y() * bp.x();
const auto d_theta = std::atan2(cross, dot);
return std::array{dist, d_theta};
};
auto circle_cpm = [&](auto index) {
const auto bulge = this->bulges[index];
const auto a = this->vertices[index];
const auto b = this->vertices[index + 1];
const Eigen::Vector2d half_ab = 0.5 * (a + b);
const Eigen::Vector2d bisec_vec = 0.5 * Eigen::Vector2d{a.y() - b.y(), b.x() - a.x()};
const Eigen::Vector2d center = half_ab + bisec_vec / bulge;
Eigen::Vector2d ca = a - center;
Eigen::Vector2d cb = b - center;
Eigen::Vector2d cp = p - center;
Eigen::Vector2d pa = a - p;
Eigen::Vector2d pb = b - p;
const auto D = cp.norm(); // 查询点到圆心的距离
const auto r = ca.norm(); // 圆弧半径
const auto half_theta = std::atan(bulge);
if (D <= epsilon) return std::array{r, half_theta * 2};
// 计算GWN
const auto gwn_main = half_theta;
const auto r_mul_D = r * D, r_plus_D = r + D, abs_r_sub_D = std::abs(r - D);
auto gwn = gwn_main;
if (abs_r_sub_D > epsilon) {
auto dot = ca.dot(cp);
auto cross = cp.x() * ca.y() - ca.x() * cp.y();
auto Y = r_plus_D * cross, X = abs_r_sub_D * (r_mul_D + dot);
auto Ga = std::atan2(Y, X);
if (std::abs(X) <= epsilon && std::abs(Y) <= epsilon) Ga = std::copysign(pi_div_2, -half_theta);
dot = cb.dot(cp);
cross = cp.x() * cb.y() - cb.x() * cp.y();
Y = r_plus_D * cross;
X = abs_r_sub_D * (r_mul_D + dot);
auto Gb = std::atan2(Y, X);
if (std::abs(X) <= epsilon && std::abs(Y) <= epsilon) Gb = std::copysign(pi_div_2, -half_theta);
auto gwn_fix = Gb - Ga;
if (gwn_fix * half_theta < 0) gwn_fix += std::copysign(pi, half_theta);
gwn += (r > D) ? gwn_fix : -gwn_fix;
}
return std::array{std::min({abs_r_sub_D, pa.norm(), pb.norm()}), gwn};
};
// 单次遍历
std::vector<size_t> indices(this->bulges.size());
std::iota(indices.begin(), indices.end(), 0);
auto [min_dist, gwn] = std::transform_reduce(
indices.begin(),
indices.end(),
std::array{std::numeric_limits<double>::max(), .0},
[](auto&& lhs, auto&& rhs) {
const auto gwn = lhs[1] + rhs[1];
const auto dist = std::min(lhs[0], rhs[0]);
return std::array{dist, gwn};
},
[&](size_t i) { return (std::abs(this->bulges[i]) <= epsilon) ? line_cpm(i) : circle_cpm(i); });
// if (p.norm() > 1 && std::abs(gwn) / two_pi != .0) abort();
return std::abs(gwn) / two_pi < 1. ? min_dist : -min_dist;
}
// double polyline_pattern::eval_sdf(Eigen::Vector2d p) const
// {
// auto line_cpm = [&](auto index) {
// const auto a = this->vertices[index];
// const auto b = this->vertices[index + 1];
// const Eigen::Vector2d line_vec = b - a;
// const Eigen::Vector2d ap = p - a;
// const Eigen::Vector2d bp = p - b;
// // 计算投影参数
// const auto t = std::clamp(line_vec.dot(ap) / line_vec.squaredNorm(), .0, 1.);
// const Eigen::Vector2d proj = a + t * line_vec;
// const auto dist = (p - proj).norm();
// // 计算GWN
// const auto dot = ap.dot(bp);
// const auto cross = ap.x() * bp.y() - ap.y() * bp.x();
// const auto d_theta = std::atan2(cross, dot);
// return std::array{dist, d_theta, proj.x(), proj.y()};
// };
// auto circle_cpm = [&](auto index) {
// const auto bulge = this->bulges[index];
// const auto a = this->vertices[index];
// const auto b = this->vertices[index + 1];
// const Eigen::Vector2d half_ab = 0.5 * (a + b);
// const Eigen::Vector2d bisec_vec = 0.5 * Eigen::Vector2d{a.y() - b.y(), b.x() - a.x()};
// const Eigen::Vector2d center = half_ab + bisec_vec / bulge;
// Eigen::Vector2d ca = a - center;
// Eigen::Vector2d cb = b - center;
// Eigen::Vector2d cp = p - center;
// Eigen::Vector2d pa = a - p;
// Eigen::Vector2d pb = b - p;
// const auto D = cp.norm(); // 查询点到圆心的距离
// const auto r = ca.norm(); // 圆弧半径
// const auto half_theta = std::atan(bulge);
// if (D < epsilon) return std::array{r, half_theta * 2, a.x(), a.y()};
// // 计算GWN
// const auto gwn_main = half_theta;
// const auto r_mul_D = r * D, r_plus_D = r + D, abs_r_sub_D = std::abs(r - D);
// const auto cross1 = cp.x() * ca.y() - ca.x() * cp.y();
// const auto cross2 = cp.x() * cb.y() - cb.x() * cp.y();
// auto gwn = gwn_main;
// if (abs_r_sub_D > epsilon) {
// auto dot = ca.dot(cp);
// const auto Ga = std::atan2(r_plus_D * cross1, abs_r_sub_D * (r_mul_D + dot));
// dot = cb.dot(cp);
// const auto Gb = std::atan2(r_plus_D * cross2, abs_r_sub_D * (r_mul_D + dot));
// auto gwn_fix = Gb - Ga;
// if (gwn_fix * half_theta < 0) gwn_fix += std::copysign(pi, half_theta);
// gwn += (r > D) ? gwn_fix : -gwn_fix;
// }
// cp /= D;
// ca /= r;
// cb /= r;
// // 原理:根据叉乘符号判断方向,不论何种情况cp都应该在ca和cb之间
// if (cross1 * cross2 > 0) {
// // 在圆弧范围内
// return std::array{D - r, gwn, center.x() + cp.x(), center.y() + cp.y()};
// } else {
// // 最近点为圆弧端点
// if (auto pa_norm = pa.norm(), pb_norm = pb.norm(); pa_norm < pb_norm)
// return std::array{pa_norm, gwn, a.x(), a.y()};
// else
// return std::array{pa_norm, gwn, b.x(), b.y()};
// }
// };
// // 单次遍历
// std::vector<size_t> indices(this->bulges.size());
// std::iota(indices.begin(), indices.end(), 0);
// auto [min_dist, gwn, x, y] = std::transform_reduce(
// indices.begin(),
// indices.end(),
// std::array{std::numeric_limits<double>::max(), .0, .0, .0},
// [](auto&& lhs, auto&& rhs) {
// const auto gwn = lhs[1] + rhs[1];
// if (lhs[0] < rhs[0])
// return std::array{lhs[0], gwn, lhs[2], lhs[3]};
// else
// return std::array{rhs[0], gwn, rhs[2], rhs[3]};
// },
// [&](size_t i) { return (std::abs(this->bulges[i]) <= epsilon) ? line_cpm(i) : circle_cpm(i); });
// return {
// // std::abs(gwn) < two_pi * epsilon,
// std::abs(gwn) == 0.,
// Eigen::Vector2d{x, y}
// };
// }
} // namespace internal