use neco_nurbs::NurbsSurface3D;
#[derive(Clone, Debug)]
pub struct BezierPatch {
pub degree_u: usize,
pub degree_v: usize,
pub control_points: Vec<Vec<[f64; 3]>>,
pub weights: Vec<Vec<f64>>,
pub u_min: f64,
pub u_max: f64,
pub v_min: f64,
pub v_max: f64,
}
impl BezierPatch {
pub fn evaluate(&self, u: f64, v: f64) -> [f64; 3] {
let p = self.degree_u;
let q = self.degree_v;
let mut knots_u = vec![self.u_min; p + 1];
knots_u.extend(vec![self.u_max; p + 1]);
let mut knots_v = vec![self.v_min; q + 1];
knots_v.extend(vec![self.v_max; q + 1]);
let surf = NurbsSurface3D {
degree_u: p,
degree_v: q,
control_points: self.control_points.clone(),
weights: self.weights.clone(),
knots_u,
knots_v,
};
surf.evaluate(u, v)
}
pub fn aabb(&self) -> ([f64; 3], [f64; 3]) {
let mut min_x = f64::INFINITY;
let mut min_y = f64::INFINITY;
let mut min_z = f64::INFINITY;
let mut max_x = f64::NEG_INFINITY;
let mut max_y = f64::NEG_INFINITY;
let mut max_z = f64::NEG_INFINITY;
for row in &self.control_points {
for pt in row {
min_x = min_x.min(pt[0]);
min_y = min_y.min(pt[1]);
min_z = min_z.min(pt[2]);
max_x = max_x.max(pt[0]);
max_y = max_y.max(pt[1]);
max_z = max_z.max(pt[2]);
}
}
([min_x, min_y, min_z], [max_x, max_y, max_z])
}
pub fn u_range(&self) -> (f64, f64) {
(self.u_min, self.u_max)
}
pub fn v_range(&self) -> (f64, f64) {
(self.v_min, self.v_max)
}
}
fn collect_internal_knots(knots: &[f64], degree: usize, n_cp: usize) -> Vec<(f64, usize)> {
let t_min = knots[degree];
let t_max = knots[n_cp];
let mut result: Vec<(f64, usize)> = Vec::new();
let mut i = degree + 1;
while i < knots.len() - degree - 1 {
let t = knots[i];
if t > t_min && t < t_max {
let mut mult = 0;
let mut j = i;
while j < knots.len() && (knots[j] - t).abs() < 1e-14 {
mult += 1;
j += 1;
}
result.push((t, mult));
i = j;
} else {
i += 1;
}
}
result
}
pub fn decompose_to_bezier_patches(surface: &NurbsSurface3D) -> Vec<BezierPatch> {
let p = surface.degree_u;
let q = surface.degree_v;
let mut surf = surface.clone();
let internal_u = collect_internal_knots(&surf.knots_u, p, surf.control_points.len());
for (t, mult) in &internal_u {
let insertions_needed = p - mult;
for _ in 0..insertions_needed {
surf = surf.insert_knot_u(*t);
}
}
let internal_v = collect_internal_knots(&surf.knots_v, q, surf.control_points[0].len());
for (t, mult) in &internal_v {
let insertions_needed = q - mult;
for _ in 0..insertions_needed {
surf = surf.insert_knot_v(*t);
}
}
let n_u = surf.control_points.len();
let n_v = surf.control_points[0].len();
let num_spans_u = (n_u - 1) / p;
let num_spans_v = (n_v - 1) / q;
if num_spans_u == 0 || num_spans_v == 0 {
return vec![BezierPatch {
degree_u: p,
degree_v: q,
control_points: surf.control_points.clone(),
weights: surf.weights.clone(),
u_min: surface.knots_u[p],
u_max: surface.knots_u[surface.control_points.len()],
v_min: surface.knots_v[q],
v_max: surface.knots_v[surface.control_points[0].len()],
}];
}
let mut patches = Vec::with_capacity(num_spans_u * num_spans_v);
let cp = &surf.control_points;
let ws = &surf.weights;
for su in 0..num_spans_u {
let u_start_cp = su * p;
let u_end_cp = u_start_cp + p + 1;
if u_end_cp > n_u {
break;
}
let u_min = surf.knots_u[u_start_cp + p];
let u_max = surf.knots_u[u_end_cp];
for sv in 0..num_spans_v {
let v_start_cp = sv * q;
let v_end_cp = v_start_cp + q + 1;
if v_end_cp > n_v {
break;
}
let v_min = surf.knots_v[v_start_cp + q];
let v_max = surf.knots_v[v_end_cp];
let mut cp_block = Vec::with_capacity(p + 1);
let mut w_block = Vec::with_capacity(p + 1);
for i in u_start_cp..u_end_cp {
cp_block.push(cp[i][v_start_cp..v_end_cp].to_vec());
w_block.push(ws[i][v_start_cp..v_end_cp].to_vec());
}
patches.push(BezierPatch {
degree_u: p,
degree_v: q,
control_points: cp_block,
weights: w_block,
u_min,
u_max,
v_min,
v_max,
});
}
}
patches
}