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.

135 lines
7.1 KiB

#include "internal_primitive_desc.hpp"
namespace internal
{
extrude_helixline::extrude_helixline(const extrude_helixline_descriptor_t &desc, aabb_t<> &aabb)
: axis(desc.axis, this->axis_to_world, aabb)
{
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.height //
+ this->axis.radius * this->axis.total_theta * matrix_handle.col(1))
.normalized();
const Eigen::Vector3d B = N.cross(T);
aabb_t<2> profile_aabb{};
this->profile.build_as_profile(desc.profiles[0], B, N, matrix_handle.col(3), profile_aabb);
const auto profile_max_extent = profile_aabb.max().cwiseMax(profile_aabb.min().cwiseAbs());
aabb.min() -= Eigen::Vector3d{profile_max_extent.x(), profile_max_extent.x(), profile_max_extent.y()};
aabb.max() += Eigen::Vector3d{profile_max_extent.x(), profile_max_extent.x(), profile_max_extent.y()};
aabb.transform(this->axis_to_world);
}
extrude_helixline::extrude_helixline(extrude_helixline_descriptor_t &&desc, aabb_t<> &aabb)
: axis(std::move(desc.axis), this->axis_to_world, aabb)
{
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.height //
+ this->axis.radius * this->axis.total_theta * matrix_handle.col(1))
.normalized();
const Eigen::Vector3d B = N.cross(T);
aabb_t<2> profile_aabb{};
this->profile.build_as_profile(std::move(desc.profiles[0]), B, N, matrix_handle.col(3), profile_aabb);
const auto profile_max_extent = profile_aabb.max().cwiseMax(profile_aabb.min().cwiseAbs());
aabb.min() -= Eigen::Vector3d{profile_max_extent.x(), profile_max_extent.x(), profile_max_extent.y()};
aabb.max() += Eigen::Vector3d{profile_max_extent.x(), profile_max_extent.x(), profile_max_extent.y()};
aabb.transform(this->axis_to_world);
}
static inline auto get_local_TBN(const helixline &line, const Eigen::Ref<const Eigen::Vector3d> &q, double t)
{
Eigen::Transform<double, 3, Eigen::Isometry> TBN{};
auto &handle = TBN.matrix();
const auto T = line.calculate_tangent(t);
const auto N = line.calculate_normal(t);
const auto B = N.cross(T);
6 months ago
handle.col(0).topRows<3>() = std::move(N);
handle.col(1).topRows<3>() = std::move(B);
handle.col(2).topRows<3>() = std::move(T);
handle.col(3).topRows<3>() = std::move(q);
return TBN;
}
template <typename Routine>
static inline process_return_type_t<Routine> evaluate(const extrude_helixline &desc,
[[maybe_unused]] const Eigen::Ref<const Eigen::Vector3d> &point)
{
const auto world_to_axis = inversed_affine_transform(desc.axis_to_world);
const auto local_p = world_to_axis * Eigen::Vector4d{point[0], point[1], point[2], 1};
const auto axis_closest_param = desc.axis.calculate_closest_param(local_p.topRows<3>());
const auto TBN = get_local_TBN(desc.axis, axis_closest_param.point, axis_closest_param.t);
const auto inv_TBN = inversed_affine_transform(TBN);
// TODO: add support for non-parallel (ray vs. profile plane) case
// for now just assume that profile plane is parallel to axis
6 months ago
auto [profile_closest_param, dist] = desc.profile.calculate_closest_param((inv_TBN * local_p).topRows<3>());
6 months ago
bool isOut = desc.profile.pmc((inv_TBN * local_p).topRows<2>(), profile_closest_param);
profile_closest_param.point = TBN * profile_closest_param.point;
if (!isOut) {
// inside or projected point inside
const auto local_p_3d = local_p.topRows<3>() / local_p[3];
if (!desc.axis.isEnd(axis_closest_param.t) || axis_closest_param.is_peak_value) {
// inside
// max() with two planes
dist = -dist;
Eigen::Vector3d start_tangent = desc.axis.calculate_tangent(0);
auto sdf_start_plane = -start_tangent.dot(local_p_3d);
if (sdf_start_plane > dist) {
dist = sdf_start_plane;
profile_closest_param.point = local_p_3d + start_tangent * dist;
} else {
Eigen::Vector3d final_v = desc.axis.evaluate(1);
Eigen::Vector3d final_tangent = desc.axis.calculate_tangent(1);
auto sdf_end_plane = final_tangent.dot(local_p_3d - final_v);
if (sdf_end_plane > dist) {
dist = sdf_end_plane;
profile_closest_param.point = local_p_3d - final_tangent * dist;
}
}
} else {
// outside, must closer to the plane
if (axis_closest_param.t < EPSILON) {
Eigen::Vector3d start_tangent = desc.axis.calculate_tangent(0);
auto sdf_start_plane = -start_tangent.dot(local_p_3d);
dist = sdf_start_plane;
profile_closest_param.point = local_p_3d + start_tangent * dist;
} else {
Eigen::Vector3d final_v = desc.axis.evaluate(1);
Eigen::Vector3d final_tangent = desc.axis.calculate_tangent(1);
auto sdf_end_plane = final_tangent.dot(local_p_3d - final_v);
dist = sdf_end_plane;
profile_closest_param.point = local_p_3d - final_tangent * dist;
}
}
} else {
// outside, nothing to do
}
6 months ago
// if (!desc.axis.isEnd(axis_closest_param.t) || axis_closest_param.is_peak_value) {
// // if !is_peak_value, the point is two sides away, and pmc is always true
// dist *= desc.profile.pmc((inv_TBN * local_p).topRows<2>(), profile_closest_param) * 2 - 1;
// }
// transform closest point to world space
// the distance should be unchanged during transformation, since no scaling is involved
if constexpr (std::is_same_v<Routine, evaluation_routine_tag>)
return dist;
else
return desc.axis_to_world * TBN * profile_closest_param.point;
}
double extrude_helixline::evaluate_sdf([[maybe_unused]] const Eigen::Ref<const Eigen::Vector3d> &point) const
{
return evaluate<evaluation_routine_tag>(*this, point);
}
Eigen::Vector3d extrude_helixline::evaluate_cpm([[maybe_unused]] const Eigen::Ref<const Eigen::Vector3d> &point) const
{
return evaluate<closest_point_routine_tag>(*this, point);
}
} // namespace internal