|
|
|
@ -8,30 +8,30 @@ |
|
|
|
namespace internal |
|
|
|
{ |
|
|
|
|
|
|
|
double integrator_t::calculate(int gauss_order, double (*func)(double u, double v, |
|
|
|
const Eigen::Vector3d& p, |
|
|
|
const Eigen::Vector3d& dU, |
|
|
|
const Eigen::Vector3d& dV)) const |
|
|
|
double integrator_t::calculate( |
|
|
|
int gauss_order, |
|
|
|
double (*func)(double u, double v, const Eigen::Vector3d& p, const Eigen::Vector3d& dU, const Eigen::Vector3d& dV)) const |
|
|
|
{ |
|
|
|
double total_integral = 0.0; |
|
|
|
for (const auto& [subface_index, param_plane] : m_uv_planes) { |
|
|
|
const auto& subface = m_subfaces[subface_index].object_ptr.get(); |
|
|
|
total_integral += calculate_one_subface(subface, param_plane, gauss_order, func); |
|
|
|
const auto& subface = m_subfaces[subface_index].object_ptr.get(); |
|
|
|
total_integral += calculate_one_subface(subface, param_plane, gauss_order, func); |
|
|
|
} |
|
|
|
return total_integral; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
double integrator_t::calculate_one_subface(const subface& subface, const parametric_plane_t& param_plane, int gauss_order, double (*func)(double u, double v, |
|
|
|
const Eigen::Vector3d& p, |
|
|
|
const Eigen::Vector3d& dU, |
|
|
|
const Eigen::Vector3d& dV)) const |
|
|
|
double integrator_t::calculate_one_subface( |
|
|
|
const subface& subface, |
|
|
|
const parametric_plane_t& param_plane, |
|
|
|
int gauss_order, |
|
|
|
double (*func)(double u, double v, const Eigen::Vector3d& p, const Eigen::Vector3d& dU, const Eigen::Vector3d& dV)) const |
|
|
|
{ |
|
|
|
auto solver = subface.fetch_solver_evaluator(); |
|
|
|
auto solver = subface.fetch_solver_evaluator(); |
|
|
|
// Gaussian integration in u direction
|
|
|
|
auto u_integrand = [&](double u) { |
|
|
|
// Find exact v intersections for each u
|
|
|
|
stl_vector_mp<double> v_breaks = find_v_intersections_at_u(subface, param_plane, u);; |
|
|
|
stl_vector_mp<double> v_breaks = find_v_intersections_at_u(subface, param_plane, u); |
|
|
|
; |
|
|
|
|
|
|
|
// Gaussian integration in v direction
|
|
|
|
auto v_integrand = [&](double v) { |
|
|
|
@ -39,25 +39,25 @@ double integrator_t::calculate_one_subface(const subface& subface, const paramet |
|
|
|
if (is_point_inside_domain(subface, param_plane, u, v)) { |
|
|
|
try { |
|
|
|
// Get evaluators for both directions
|
|
|
|
auto eval_du = subface.fetch_curve_constraint_evaluator(parameter_v_t{}, v); // Fix v, vary u → ∂/∂u
|
|
|
|
auto eval_dv = subface.fetch_curve_constraint_evaluator(parameter_u_t{}, u); // Fix u, vary v → ∂/∂v
|
|
|
|
auto eval_du = subface.fetch_curve_constraint_evaluator(parameter_v_t{}, v); // Fix v, vary u → ∂/∂u
|
|
|
|
auto eval_dv = subface.fetch_curve_constraint_evaluator(parameter_u_t{}, u); // Fix u, vary v → ∂/∂v
|
|
|
|
|
|
|
|
auto res_u = eval_du(u); // f(u,v), grad_f = ∂r/∂u
|
|
|
|
auto res_v = eval_dv(v); // f(u,v), grad_f = ∂r/∂v
|
|
|
|
auto res_u = eval_du(u); // f(u,v), grad_f = ∂r/∂u
|
|
|
|
auto res_v = eval_dv(v); // f(u,v), grad_f = ∂r/∂v
|
|
|
|
|
|
|
|
Eigen::Vector3d p = res_u.f.template head<3>(); // Point (x,y,z)
|
|
|
|
Eigen::Vector3d dU = res_u.grad_f.template head<3>(); // ∂r/∂u
|
|
|
|
Eigen::Vector3d dV = res_v.grad_f.template head<3>(); // ∂r/∂v
|
|
|
|
Eigen::Vector3d p = res_u.f.template head<3>(); // Point (x,y,z)
|
|
|
|
Eigen::Vector3d dU = res_u.grad_f.template head<3>(); // ∂r/∂u
|
|
|
|
Eigen::Vector3d dV = res_v.grad_f.template head<3>(); // ∂r/∂v
|
|
|
|
|
|
|
|
// Area element: ||dU × dV||
|
|
|
|
Eigen::Vector3d cross = dU.cross(dV); |
|
|
|
double jacobian = cross.norm(); // Jacobian (area scaling factor)
|
|
|
|
Eigen::Vector3d cross = dU.cross(dV); |
|
|
|
double jacobian = cross.norm(); // Jacobian (area scaling factor)
|
|
|
|
return func(u, v, p, dU, dV) * jacobian; |
|
|
|
} catch (...) { |
|
|
|
return 0.0; // Skip singular points
|
|
|
|
return 0.0; // Skip singular points
|
|
|
|
} |
|
|
|
} |
|
|
|
return 0.0; // Point not in domain
|
|
|
|
return 0.0; // Point not in domain
|
|
|
|
}; |
|
|
|
|
|
|
|
double v_integral = 0.0; |
|
|
|
@ -78,14 +78,14 @@ double integrator_t::calculate_one_subface(const subface& subface, const paramet |
|
|
|
}; |
|
|
|
|
|
|
|
// Integrate in u direction
|
|
|
|
const auto& u_breaks = compute_u_breaks(param_plane,0.0, 1.0); |
|
|
|
double integral = 0.0; |
|
|
|
const auto& u_breaks = compute_u_breaks(param_plane, 0.0, 1.0); |
|
|
|
double integral = 0.0; |
|
|
|
for (size_t i = 0; i < u_breaks.size() - 1; ++i) { |
|
|
|
double a = u_breaks[i]; |
|
|
|
double b = u_breaks[i + 1]; |
|
|
|
|
|
|
|
double mid_u = (a + b) / 2.0; |
|
|
|
auto v_intersections = find_v_intersections_at_u(subface, param_plane,mid_u); |
|
|
|
auto v_intersections = find_v_intersections_at_u(subface, param_plane, mid_u); |
|
|
|
|
|
|
|
if (!v_intersections.empty()) { |
|
|
|
integral += integrate_1D(a, b, u_integrand, gauss_order, is_u_near_singularity(mid_u)); |
|
|
|
@ -103,10 +103,7 @@ double integrator_t::calculate_one_subface(const subface& subface, const paramet |
|
|
|
* TODO: Use a tolerance-based approach to avoid floating-point precision issues |
|
|
|
* when inserting u-values (e.g., merge values within 1e-12). |
|
|
|
*/ |
|
|
|
stl_vector_mp<double> integrator_t::compute_u_breaks( |
|
|
|
const parametric_plane_t& param_plane, |
|
|
|
double u_min, |
|
|
|
double u_max) const |
|
|
|
stl_vector_mp<double> integrator_t::compute_u_breaks(const parametric_plane_t& param_plane, double u_min, double u_max) const |
|
|
|
{ |
|
|
|
std::set<double> break_set; |
|
|
|
|
|
|
|
@ -119,41 +116,39 @@ stl_vector_mp<double> integrator_t::compute_u_breaks( |
|
|
|
const auto& vertices = param_plane.chain_vertices[i]; |
|
|
|
auto& vertex_flags = param_plane.vertex_special_flags[i]; |
|
|
|
for (size_t j = 0; j < vertices.size(); ++j) { |
|
|
|
if (vertex_flags[j]) { break_set.insert( vertices[j].x());} // Special vertex u
|
|
|
|
if (vertex_flags[j]) { break_set.insert(vertices[j].x()); } // Special vertex u
|
|
|
|
} |
|
|
|
} |
|
|
|
// Return as vector (sorted and unique due to set)
|
|
|
|
return stl_vector_mp<double>(break_set.begin(), break_set.end()); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
stl_vector_mp<double> integrator_t::find_v_intersections_at_u( |
|
|
|
const subface& subface, |
|
|
|
const parametric_plane_t& param_plane, |
|
|
|
double u_val) const |
|
|
|
stl_vector_mp<double> integrator_t::find_v_intersections_at_u(const subface& subface, |
|
|
|
const parametric_plane_t& param_plane, |
|
|
|
double u_val) const |
|
|
|
{ |
|
|
|
stl_vector_mp<double> intersections; |
|
|
|
|
|
|
|
// Iterate over each boundary chain
|
|
|
|
for (const auto& chain : param_plane.chain_vertices) { |
|
|
|
const size_t n_vertices = chain.size(); |
|
|
|
if (n_vertices < 2) continue; // Skip degenerate chains
|
|
|
|
if (n_vertices < 2) continue; // Skip degenerate chains
|
|
|
|
|
|
|
|
// Iterate over each edge in the chain (including closing edge if desired)
|
|
|
|
for (size_t i = 0; i < n_vertices; ++i) { |
|
|
|
size_t j = (i + 1) % n_vertices; // Next vertex index (wraps around for closed chain)
|
|
|
|
size_t j = (i + 1) % n_vertices; // Next vertex index (wraps around for closed chain)
|
|
|
|
|
|
|
|
const Eigen::Vector2d& v1 = chain[i]; // Current vertex: (u1, v1)
|
|
|
|
const Eigen::Vector2d& v2 = chain[j]; // Next vertex: (u2, v2)
|
|
|
|
const Eigen::Vector2d& v1 = chain[i]; // Current vertex: (u1, v1)
|
|
|
|
const Eigen::Vector2d& v2 = chain[j]; // Next vertex: (u2, v2)
|
|
|
|
|
|
|
|
double u1 = v1.x(), v1_val = v1.y(); |
|
|
|
double u2 = v2.x(), v2_val = v2.y(); |
|
|
|
|
|
|
|
// Classify position relative to u = u_val: -1 (left), 0 (on), +1 (right)
|
|
|
|
const double eps = 1e-12; |
|
|
|
auto sign_cmp = [u_val, eps](double u) -> int { |
|
|
|
const double eps = 1e-12; |
|
|
|
auto sign_cmp = [u_val, eps](double u) -> int { |
|
|
|
if (u < u_val - eps) return -1; |
|
|
|
if (u > u_val + eps) return 1; |
|
|
|
if (u > u_val + eps) return 1; |
|
|
|
return 0; |
|
|
|
}; |
|
|
|
|
|
|
|
@ -162,9 +157,7 @@ stl_vector_mp<double> integrator_t::find_v_intersections_at_u( |
|
|
|
int pos2 = sign_cmp(u2); |
|
|
|
|
|
|
|
// Case 1: Both endpoints on the same side (and not on the line) → no intersection
|
|
|
|
if (pos1 * pos2 > 0) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
if (pos1 * pos2 > 0) { continue; } |
|
|
|
|
|
|
|
// Case 2: Both endpoints lie exactly on u = u_val → add both v-values
|
|
|
|
if (pos1 == 0 && pos2 == 0) { |
|
|
|
@ -178,12 +171,11 @@ stl_vector_mp<double> integrator_t::find_v_intersections_at_u( |
|
|
|
intersections.push_back(v1_val); |
|
|
|
intersections.push_back(v2_val); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
} else { |
|
|
|
// General case: non-vertical segment crossing u = u_val
|
|
|
|
// Compute linear interpolation parameter t
|
|
|
|
double t = (u_val - u1) / (u2 - u1); |
|
|
|
double v_initial = v1_val + t * (v2_val - v1_val); // Initial guess for v
|
|
|
|
double t = (u_val - u1) / (u2 - u1); |
|
|
|
double v_initial = v1_val + t * (v2_val - v1_val); // Initial guess for v
|
|
|
|
|
|
|
|
// Fetch evaluators from subface for constraint solving
|
|
|
|
auto curve_evaluator = subface.fetch_curve_constraint_evaluator(parameter_u_t{}, u_val); |
|
|
|
@ -192,7 +184,7 @@ stl_vector_mp<double> integrator_t::find_v_intersections_at_u( |
|
|
|
// Define target function: v ↦ residual of implicit equation
|
|
|
|
auto target_function = [&](double v) -> internal::implicit_equation_intermediate { |
|
|
|
constraint_curve_intermediate temp_res = curve_evaluator(v); |
|
|
|
auto full_res = solver_evaluator(std::move(temp_res)); |
|
|
|
auto full_res = solver_evaluator(std::move(temp_res)); |
|
|
|
return std::get<internal::implicit_equation_intermediate>(full_res); |
|
|
|
}; |
|
|
|
|
|
|
|
@ -209,9 +201,7 @@ stl_vector_mp<double> integrator_t::find_v_intersections_at_u( |
|
|
|
} |
|
|
|
|
|
|
|
// Final step: sort and remove duplicates within tolerance
|
|
|
|
if (!intersections.empty()) { |
|
|
|
sort_and_unique_with_tol(intersections, 1e-8); |
|
|
|
} |
|
|
|
if (!intersections.empty()) { sort_and_unique_with_tol(intersections, 1e-8); } |
|
|
|
|
|
|
|
return intersections; |
|
|
|
} |
|
|
|
@ -223,33 +213,28 @@ stl_vector_mp<double> integrator_t::find_v_intersections_at_u( |
|
|
|
NOTE: when v_intersect - v < threshold, further checks are required to accurately determine if the intersection point lies |
|
|
|
precisely on the boundary segment. |
|
|
|
*/ |
|
|
|
bool integrator_t::is_point_inside_domain( |
|
|
|
const subface& subface, |
|
|
|
const parametric_plane_t& param_plane, |
|
|
|
double u, |
|
|
|
double v) const |
|
|
|
bool integrator_t::is_point_inside_domain(const subface& subface, |
|
|
|
const parametric_plane_t& param_plane, |
|
|
|
double u, |
|
|
|
double v) const |
|
|
|
{ |
|
|
|
auto intersections = find_v_intersections_at_u(subface, param_plane, u); |
|
|
|
const double tol_near = 1e-8; |
|
|
|
const double tol_above = 1e-12; |
|
|
|
auto intersections = find_v_intersections_at_u(subface, param_plane, u); |
|
|
|
const double tol_near = 1e-8; |
|
|
|
const double tol_above = 1e-12; |
|
|
|
|
|
|
|
uint32_t count = 0; |
|
|
|
for (double v_int : intersections) { |
|
|
|
double diff = v_int - v; |
|
|
|
if (diff > tol_above) { |
|
|
|
count++; |
|
|
|
} |
|
|
|
else if (std::abs(diff) < tol_near) { |
|
|
|
} else if (std::abs(diff) < tol_near) { |
|
|
|
return true; // on boundary → inside
|
|
|
|
} |
|
|
|
} |
|
|
|
return (count % 2) == 1; |
|
|
|
} |
|
|
|
|
|
|
|
bool integrator_t::is_u_near_singularity(double u, double tol) const |
|
|
|
{ |
|
|
|
return false; |
|
|
|
} |
|
|
|
bool integrator_t::is_u_near_singularity(double u, double tol) const { return false; } |
|
|
|
|
|
|
|
void integrator_t::sort_and_unique_with_tol(stl_vector_mp<double>& vec, double epsilon) const |
|
|
|
{ |
|
|
|
@ -269,17 +254,16 @@ void integrator_t::sort_and_unique_with_tol(stl_vector_mp<double>& vec, double e |
|
|
|
} |
|
|
|
|
|
|
|
// Only accepts functions that return implicit_equation_intermediate
|
|
|
|
double newton_method( |
|
|
|
const std::function<internal::implicit_equation_intermediate(double)>& F, |
|
|
|
double v_initial, |
|
|
|
double tolerance, |
|
|
|
int max_iterations) |
|
|
|
double newton_method(const std::function<internal::implicit_equation_intermediate(double)>& F, |
|
|
|
double v_initial, |
|
|
|
double tolerance, |
|
|
|
int max_iterations) |
|
|
|
{ |
|
|
|
double v = v_initial; |
|
|
|
|
|
|
|
for (int i = 0; i < max_iterations; ++i) { |
|
|
|
auto res = F(v); // Known type: implicit_equation_intermediate
|
|
|
|
double f_val = res.f; |
|
|
|
auto res = F(v); // Known type: implicit_equation_intermediate
|
|
|
|
double f_val = res.f; |
|
|
|
double df_val = res.df; |
|
|
|
|
|
|
|
if (std::abs(f_val) < tolerance) { |
|
|
|
|