use crate::MathError;
use crate::nurbs::surface::NurbsSurface;
use crate::vec::Vec3;
use super::chaining::build_curves_from_points;
use super::{IntersectionCurve, IntersectionPoint, MAX_NEWTON_ITER};
pub fn intersect_plane_nurbs(
surface: &NurbsSurface,
plane_normal: Vec3,
plane_d: f64,
samples: usize,
) -> Result<Vec<IntersectionCurve>, MathError> {
let n = samples.max(10);
let mut distances = vec![vec![0.0_f64; n]; n];
let (u_min, u_max) = surface.domain_u();
let (v_min, v_max) = surface.domain_v();
#[allow(clippy::cast_precision_loss)]
let u_step = (u_max - u_min) / (n - 1) as f64;
#[allow(clippy::cast_precision_loss)]
let v_step = (v_max - v_min) / (n - 1) as f64;
#[allow(clippy::cast_precision_loss)]
for (i, row) in distances.iter_mut().enumerate() {
let u = u_min + i as f64 * u_step;
for (j, dist) in row.iter_mut().enumerate() {
let v = v_min + j as f64 * v_step;
let pt = surface.evaluate(u, v);
let pt_vec = Vec3::new(pt.x(), pt.y(), pt.z());
*dist = plane_normal.dot(pt_vec) - plane_d;
}
}
let mut crossing_points: Vec<(f64, f64, super::Point3)> = Vec::new();
#[allow(clippy::cast_precision_loss)]
for i in 0..n - 1 {
for j in 0..n - 1 {
let d00 = distances[i][j];
let d10 = distances[i + 1][j];
let d01 = distances[i][j + 1];
let d11 = distances[i + 1][j + 1];
let u0 = u_min + i as f64 * u_step;
let u1 = u_min + (i + 1) as f64 * u_step;
let v0 = v_min + j as f64 * v_step;
let v1 = v_min + (j + 1) as f64 * v_step;
if d00 * d10 < 0.0 {
let t = d00 / (d00 - d10);
let u = u0.mul_add(1.0 - t, u1 * t);
let pt = surface.evaluate(u, v0);
crossing_points.push((u, v0, pt));
}
if d00 * d01 < 0.0 {
let t = d00 / (d00 - d01);
let v = v0.mul_add(1.0 - t, v1 * t);
let pt = surface.evaluate(u0, v);
crossing_points.push((u0, v, pt));
}
if d01 * d11 < 0.0 {
let t = d01 / (d01 - d11);
let u = u0.mul_add(1.0 - t, u1 * t);
let pt = surface.evaluate(u, v1);
crossing_points.push((u, v1, pt));
}
if d10 * d11 < 0.0 {
let t = d10 / (d10 - d11);
let v = v0.mul_add(1.0 - t, v1 * t);
let pt = surface.evaluate(u1, v);
crossing_points.push((u1, v, pt));
}
}
}
if crossing_points.is_empty() {
return Ok(Vec::new());
}
let mut refined_points: Vec<IntersectionPoint> = Vec::new();
for (u_guess, v_guess, _pt) in &crossing_points {
if let Some(refined) =
refine_plane_surface_point(surface, plane_normal, plane_d, *u_guess, *v_guess)
{
refined_points.push(refined);
}
}
if refined_points.is_empty() {
return Ok(Vec::new());
}
let curves = build_curves_from_points(&refined_points)?;
Ok(curves)
}
fn refine_plane_surface_point(
surface: &NurbsSurface,
plane_normal: Vec3,
plane_d: f64,
u_guess: f64,
v_guess: f64,
) -> Option<IntersectionPoint> {
let mut u = u_guess;
let mut v = v_guess;
let (u_min, u_max) = surface.domain_u();
let (v_min, v_max) = surface.domain_v();
for _ in 0..MAX_NEWTON_ITER {
let pt = surface.evaluate(u, v);
let pt_vec = Vec3::new(pt.x(), pt.y(), pt.z());
let f = plane_normal.dot(pt_vec) - plane_d;
if f.abs() < 1e-12 {
return Some(IntersectionPoint {
point: pt,
param1: (u, v),
param2: (0.0, 0.0),
});
}
let derivs = surface.derivatives(u, v, 1);
let du = derivs[1][0]; let dv = derivs[0][1];
let grad_u = plane_normal.dot(du);
let grad_v = plane_normal.dot(dv);
let grad_len_sq = grad_u.mul_add(grad_u, grad_v * grad_v);
if grad_len_sq < 1e-20 {
break; }
let step_size = f / grad_len_sq;
u -= grad_u * step_size;
v -= grad_v * step_size;
u = u.clamp(u_min, u_max);
v = v.clamp(v_min, v_max);
}
let pt = surface.evaluate(u, v);
let pt_vec = Vec3::new(pt.x(), pt.y(), pt.z());
let f = plane_normal.dot(pt_vec) - plane_d;
if f.abs() < 1e-6 {
Some(IntersectionPoint {
point: pt,
param1: (u, v),
param2: (0.0, 0.0),
})
} else {
None
}
}