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.

153 lines
6.3 KiB

#pragma once
#include "evaluation_prev.hpp"
template <typename Routine>
inline auto evaluate(Routine&& tag, const constant_descriptor_t& desc, const Eigen::Ref<const Eigen::Vector3d>& point)
{
if constexpr (is_evaluation_routine_v<Routine>)
return desc.value;
else
return point;
}
template <typename Routine>
inline auto evaluate(Routine&& tag, const plane_descriptor_t& desc, const Eigen::Ref<const Eigen::Vector3d>& point)
{
auto normal = vec3d_conversion(desc.normal);
auto distance = normal.dot(point - vec3d_conversion(desc.point));
if constexpr (is_evaluation_routine_v<Routine>)
return distance;
else
return point - distance * normal;
}
template <typename Routine>
inline auto evaluate(Routine&& tag, const sphere_descriptor_t& desc, const Eigen::Ref<const Eigen::Vector3d>& point)
{
auto center = vec3d_conversion(desc.center);
auto p_vec = point - center;
auto p_vec_norm = p_vec.norm();
if constexpr (is_evaluation_routine_v<Routine>)
return p_vec_norm - desc.radius;
else
return center + (p_vec / std::abs(p_vec_norm)) * desc.radius;
}
inline auto evaluate(const cylinder_descriptor_t& desc, const Eigen::Ref<const Eigen::Vector3d>& point)
{
auto bottom_center = vec3d_conversion(desc.bottom_origion);
auto offset = vec3d_conversion(desc.offset);
const auto& radius = desc.radius;
Eigen::Vector3d ba = -offset;
Eigen::Vector3d pa = point - (bottom_center + offset);
auto baba = ba.squaredNorm();
auto paba = pa.dot(ba);
auto x = (pa * baba - ba * paba).norm() - radius * baba;
auto y = abs(paba - baba * 0.5) - baba * 0.5;
auto x2 = x * x;
auto y2 = y * y * baba;
auto d = (std::max(x, y) < 0.0) ? -std::min(x2, y2) : (((x > 0.0) ? x2 : 0.0) + ((y > 0.0) ? y2 : 0.0));
return sign(d) * std::sqrt(abs(d)) / baba;
}
inline auto evaluate(const cone_descriptor_t& desc, const Eigen::Ref<const Eigen::Vector3d>& point)
{
Eigen::Vector3d ba = vec3d_conversion(desc.bottom_point) - vec3d_conversion(desc.top_point);
Eigen::Vector3d pa = point - vec3d_conversion(desc.bottom_point);
auto rba = desc.radius2 - desc.radius1;
auto baba = ba.squaredNorm();
auto paba = pa.dot(ba) / baba;
auto papa = pa.squaredNorm();
auto x = std::sqrt(papa - paba * paba * baba);
auto cax = std::max(0.0, x - ((paba < 0.5) ? desc.radius1 : desc.radius2));
auto cay = abs(paba - 0.5) - 0.5;
auto k = rba * rba + baba;
auto f = std::clamp((rba * (x - desc.radius1) + paba * baba) / k, 0.0, 1.0);
auto cbx = x - desc.radius1 - f * rba;
auto cby = paba - f;
auto s = (cbx < 0.0 && cay < 0.0) ? -1.0 : 1.0;
return s * std::sqrt(std::min(cax * cax + cay * cay * baba, cbx * cbx + cby * cby * baba));
}
template <typename Routine>
inline auto evaluate(Routine&& tag, const box_descriptor_t& desc, const Eigen::Ref<const Eigen::Vector3d>& point)
{
// HINT: this method is not ACCURATE for OUTSIDE OF THE BOX, but it saves time
auto center = vec3d_conversion(desc.center);
auto half_size = vec3d_conversion(desc.half_size);
auto p_vec = point - center;
Eigen::Vector3d d = p_vec.cwiseAbs() - half_size;
size_t min_distance_face_mask{};
auto distance = d.maxCoeff(&min_distance_face_mask);
if constexpr (is_evaluation_routine_v<Routine>)
return distance;
else {
if (distance < 0) { // i.e. inside the box
Eigen::Vector3d result{point};
std::array dis_vec{
center[min_distance_face_mask] - half_size[min_distance_face_mask] - point[min_distance_face_mask],
center[min_distance_face_mask] + half_size[min_distance_face_mask] - point[min_distance_face_mask],
};
result[min_distance_face_mask] += (-dis_vec[0] <= dis_vec[1]) ? dis_vec[0] : dis_vec[1];
return result;
} else { // i.e. on surface or outside the box
return point.cwiseMin(center + half_size).cwiseMax(center - half_size);
}
}
}
template <typename Routine>
inline auto evaluate(Routine&& tag, const mesh_descriptor_t& desc, const Eigen::Ref<const Eigen::Vector3d>& point)
{
// Note: There is no check for out-of-bounds access to points, indexes and faces
auto points = desc.points;
auto indices = desc.indices;
auto face = desc.faces;
auto min_distance{std::numeric_limits<double>::infinity()};
uint32_t min_distance_face_index{}, min_distance_vert_start_index{};
uint32_t count{};
for (auto i = 0; i < desc.face_number; i++) {
const auto& begin_index = face[i].begin_index;
const auto& length = face[i].vertex_count;
auto point0 = vec3d_conversion(points[indices[begin_index]]);
bool flag{};
for (auto j = 1; j < length - 1; j++) {
auto point1 = vec3d_conversion(points[indices[begin_index + j]]);
auto point2 = vec3d_conversion(points[indices[begin_index + j + 1]]);
auto temp = triangle_sdf(evaluation_tag, point, point0, point1, point2);
if (temp < min_distance) {
min_distance = temp;
min_distance_face_index = i;
min_distance_vert_start_index = j;
}
if (!flag && ray_intersects_triangle(point, x_direction, point0, point1, point2)) { flag = true; }
}
if (flag) { count++; }
}
if constexpr (is_evaluation_routine_v<Routine>) {
if (min_distance < 1e-8) { return 0; }
if (count % 2 == 1) {
return -min_distance;
} else {
return min_distance;
}
} else {
const auto& begin_index = face[min_distance_face_index].begin_index;
const auto& length = face[min_distance_face_index].vertex_count;
auto point0 = vec3d_conversion(points[indices[begin_index]]);
auto point1 = vec3d_conversion(points[indices[begin_index + min_distance_vert_start_index]]);
auto point2 = vec3d_conversion(points[indices[begin_index + min_distance_vert_start_index + 1]]);
return triangle_sdf(closest_point_tag, point, point0, point1, point2);
}
}